diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-15 09:21:25 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-15 09:21:25 +0000 |
commit | 5460df5c7c35d1ff278150d830c7b9875663cf9d (patch) | |
tree | ef508d3284c713a6eb7ffc2e833f537cb8537b7b | |
parent | c84d8b937dd22fcb3b7ede1f1c1b971edd05e0de (diff) | |
parent | 2cab479e280b8349d3a193a980c68400acb18733 (diff) | |
download | ethtool-aml_tz5_341510010.tar.gz |
Snap for 11224086 from 2cab479e280b8349d3a193a980c68400acb18733 to mainline-tzdata5-releaseaml_tz5_341510070aml_tz5_341510050aml_tz5_341510010aml_tz5_341510010
Change-Id: I6a357e8402ecccb37f5f7f53757fe0ed16d74333
186 files changed, 32924 insertions, 1076 deletions
@@ -30,3 +30,5 @@ test-*.trs .*.swp *.patch +.dirstamp +tags @@ -37,44 +37,34 @@ license { cc_binary { name: "ethtool", + local_include_dirs: [ + "uapi", + "libmnl", + "libmnl/include", + ], srcs: [ - "amd8111e.c", - "at76c50x-usb.c", - "de2104x.c", - "dsa.c", - "e100.c", - "e1000.c", - "et131x.c", - "ethtool.c", - "fec.c", - "fec_8xx.c", - "fjes.c", - "ibm_emac.c", - "igb.c", - "ixgb.c", - "ixgbe.c", - "ixgbevf.c", - "lan78xx.c", - "marvell.c", - "natsemi.c", - "pcnet32.c", - "qsfp.c", - "realtek.c", - "rxclass.c", - "sfc.c", - "sff-common.c", - "sfpdiag.c", - "sfpid.c", - "smsc911x.c", - "stmmac.c", - "tg3.c", - "tse.c", - "vioc.c", - "vmxnet3.c", + "libmnl/src/*.c", + "netlink/*.c", + "[a-s]*.c", + "t[a-d]*.c", + // avoid test-*.c -- note these are shell globs, not regexps + "t[f-z]*.c", + "[u-z]*.c", ], cflags: [ "-Wno-missing-field-initializers", + "-Wno-gnu-pointer-arith", + "-Wno-gnu-variable-sized-type-not-at-end", + "-Wno-unused-parameter", + "-DETHTOOL_ENABLE_NETLINK", + // causes a fair bit of binary bloat: "-DETHTOOL_ENABLE_PRETTY_DUMP", "-DPACKAGE=\"ethtool\"", - "-DVERSION=\"5.3\"", - ] + "-DVERSION=\"6.5\"", + ], + apex_available: [ + "com.android.tethering", + "//apex_available:platform", + ], + installable: false, + min_sdk_version: "30", } @@ -1,3 +1,9 @@ ethtool is available under the terms of the GNU Public License version 2. See COPYING for details. + +-- + +libmnl is available under the terms of the GNU LESSER GENERAL PUBLIC LICENSE 2.1. + +See libmnl/COPYING for details. diff --git a/Makefile.am b/Makefile.am index 3af4d4c..ae3b667 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,20 +1,25 @@ -AM_CFLAGS = -Wall +AM_CFLAGS = -Wall -Wextra -D_POSIX_C_SOURCE=200809L +AM_CPPFLAGS = -I$(top_srcdir)/uapi LDADD = -lm man_MANS = ethtool.8 EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh sbin_PROGRAMS = ethtool -ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \ - rxclass.c +ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \ + uapi/linux/net_tstamp.h uapi/linux/if.h uapi/linux/hdlc/ioctl.h \ + rxclass.c common.c common.h \ + json_writer.c json_writer.h json_print.c json_print.h \ + list.h if ETHTOOL_ENABLE_PRETTY_DUMP ethtool_SOURCES += \ amd8111e.c de2104x.c dsa.c e100.c e1000.c et131x.c igb.c \ - fec.c fec_8xx.c ibm_emac.c ixgb.c ixgbe.c natsemi.c \ - pcnet32.c realtek.c tg3.c marvell.c vioc.c \ + fec.c fec_8xx.c fsl_enetc.c ibm_emac.c ixgb.c ixgbe.c \ + natsemi.c 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 cmis.c cmis.h bnxt.c cpsw.c lan743x.c hns3.c endif if ENABLE_BASH_COMPLETION @@ -22,12 +27,41 @@ bashcompletiondir = $(BASH_COMPLETION_DIR) dist_bashcompletion_DATA = shell-completion/bash/ethtool endif -TESTS = test-cmdline test-features -check_PROGRAMS = test-cmdline test-features +if ETHTOOL_ENABLE_NETLINK +ethtool_SOURCES += \ + netlink/netlink.c netlink/netlink.h netlink/extapi.h \ + netlink/msgbuff.c netlink/msgbuff.h netlink/nlsock.c \ + netlink/nlsock.h netlink/strset.c netlink/strset.h \ + 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/fec.c \ + netlink/stats.c netlink/mm.c \ + netlink/desc-ethtool.c netlink/desc-genlctrl.c \ + netlink/module-eeprom.c netlink/module.c netlink/rss.c \ + netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.c \ + netlink/plca.c \ + netlink/pse-pd.c \ + uapi/linux/ethtool_netlink.h \ + uapi/linux/netlink.h uapi/linux/genetlink.h \ + uapi/linux/rtnetlink.h uapi/linux/if_link.h \ + uapi/linux/if.h uapi/linux/hdlc/ioctl.h +AM_CPPFLAGS += @MNL_CFLAGS@ +LDADD += @MNL_LIBS@ +endif + +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) @@ -1,3 +1,234 @@ +Version 6.5 - September 12, 2023 + * Feature: register dump for hns3 driver (-d) + * Fix: fix fallback to ioctl for sset (-s) + * Fix: fix empty slot search in rmgr (-N) + +Version 6.4 - July 1, 2023 + * Feature: get/set Tx push buffer length (-G) + * Feature: sff-8636 and cmis: report LOL / LOS / Tx Fault (-m) + * Fix: fix duplex setting parser (-s) + * Misc: check and require C11 language standard + * Misc: clean up obsolete pre-build checks + +Version 6.3 - May 8, 2023 + * Feature: PLCA support (--[gs]et-plca-cfg, --get-plca-status) + * Feature: MAC Merge layer support (--show-mm, --set-mm) + * Feature: pass source of statistics for port stats + * Feature: get/set rx push in ringparams (-g and -G) + * Feature: coalesce tx aggregation parameters (-c and -C) + * Feature: PSE and PD devices (--show-pse, --set-pse) + * Fix: minor fixes of help text (--help) + * Fix: fix build on systems with older system headers + * Fix: fix netlink support when PLCA is not present (no option) + * Fix: fixes for issues found with gcc13 -fanalyzer + * Fix: fix return code in rxclass_rule_ins (-N) + * Fix: more robust argc/argv handling + +Version 6.2 - February 21, 2023 + * Feature: link down event statistics (no option) + * Feature: JSON output for coalesce (-c) + * Feature: new link modes (no option) + * Feature: JSON output for ring (-g) + * Feature: netlink handler for RSS get (-x) + * Fix: fix boolean value output in JSON output + * Fix: fix build errors and warnings + +Version 6.1 - December 19, 2022 + * Feature: update link mode tables + * Feature: register dump for NXP ENETC driver (-d) + * Feature: report TCP header-data split (-g) + * Feature: support new message types in pretty print + * Fix: fix compiler warnings + * Fix: man page syntax fixes + +Version 6.0 - October 10, 2022 + * Fix: advertisement modes autoselection by lanes (-s) + +Version 5.19 - August 22, 2022 + * Feature: get/set tx push (-g and -G) + * Feature: register dump support for TI CPSW (-d) + * Feature: register dump support for lan743x chipset (-d) + * Fix: fix missing sff-8472 output in netlink path (-m) + * Fix: fix EEPROM byte write (-E) + +Version 5.18 - June 14, 2022 + * Feature: get/set cqe size (-g and -G) + * Fix: fix typo in man page + * Fix: fix help text alignment + * Fix: improve attribute label (--show-fec) + +Version 5.17 - April 4, 2022 + * Feature: transceiver module power mode (--set-module) + * Feature: transceiver module extended state (--show-module) + * Feature: get/set rx buffer length (-g and -G) + * Feature: tx copybreak buffer size (--get-tunable and --set-tunable) + * Feature: JSON output for features (-k) + * Feature: support OSFP transceiver modules (-m) + * Fix: add missing free() calls (--get-tunable and --set-tunable) + +Version 5.16 - January 19, 2022 + * Feature: use memory maps for module EEPROM parsing (-m) + * Feature: show CMIS diagnostic information (-m) + * Fix: fix dumping advertised FEC modes (--show-fec) + * Fix: ignore cable test notifications from other devices (--cable-test) + * Fix: do not show duplicate options in help text (--help) + +Version 5.15 - November 9, 2021 + * Feature: new extended link substates for bad signal (no arg) + * Feature: coalesce cqe mode attributes (-c and -C) + * Fix: multiple fixes of EEPROM module data parsing (-m) + * Fix: fix condition to display MDI-X info (no arg) + +Version 5.14 - September 12, 2021 + * Feature: do not silently ignore --json if unsupported + * Feature: support new message types in pretty print + +Version 5.13 - July 9, 2021 + * Feature: netlink handler for FEC (--show-fec and --set-fec) + * Feature: FEC stats support (--show-fec) + * Feature: standard based stats support (-S) + * Feature: netlink handler for module EEPROM dump (-m) + * Feature: page, bank and i2c selection in module dump (-m) + +Version 5.12 - May 2, 2021 + * Feature: support lanes count (no option and -s) + * Fix: fix help message for master-slave parameter (-s) + * Fix: better error message for master-slave in ioctl code path + * Fix: get rid of compiler warnings in "make check" + +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 + * Feature: netlink: add netlink related UAPI header files + * Feature: netlink: introduce the netlink interface + * Feature: netlink: message buffer and composition helpers + * Feature: netlink: netlink socket wrapper and helpers + * Feature: netlink: initialize ethtool netlink socket + * Feature: netlink: add support for string sets + * Feature: netlink: add notification monitor + * Feature: netlink: add bitset helpers + * Feature: netlink: partial netlink handler for gset (no option) + * Feature: netlink: support getting wake-on-lan and debugging settings + * Feature: netlink: add basic command line parsing helpers + * Feature: netlink: add bitset command line parser handlers + * Feature: netlink: add netlink handler for sset (-s) + * Feature: netlink: support tests with netlink enabled + * Feature: netlink: add handler for permaddr (-P) + * Feature: netlink: support for pretty printing netlink messages + * Feature: netlink: message format description for ethtool netlink + * Feature: netlink: message format descriptions for genetlink control + * Feature: netlink: message format descriptions for rtnetlink + * Feature: netlink: use pretty printing for ethtool netlink messages + +Version 5.4 - January 10, 2020 + + * Feature: ethtool: implement support for Energy Detect Power Down + * Fix: fix arithmetic on pointer to void is a GNU extension warning + * Fix: fix unused parameter warnings in do_version() and show_usage() + * Fix: fix unused parameter warning in find_option() + * Fix: fix unused parameter warning in dump_eeprom() + * Fix: fix unused parameter warning in altera_tse_dump_regs() + * Fix: fix unused parameter warning in sfc_dump_regs() + * Fix: fix unused parameter warning in print_simple_table() + * Fix: fix unused parameter warning in natsemi_dump_regs() + * Fix: fix unused parameter warning in netsemi_dump_eeprom() + * Fix: fix unused parameter warning in ixgbe_dump_regs() + * Fix: fix unused parameter warning in realtek_dump_regs() + * Fix: fix unused parameter warning in lan78xx_dump_regs() + * Fix: fix unused parameter warning in {skge, sky2}_dump_regs() + * Fix: fix unused parameter warning in dsa_dump_regs() + * Fix: fix unused parameter warning in vmxnet3_dump_regs() + * Fix: fix unused parameter warning in st_{mac100, gmac}_dump_regs() + * Fix: fix unused parameter warning in ixgbevf_dump_regs() + * Fix: fix unused parameter warning in fec_8xx_dump_regs() + * Fix: fix unused parameter warning in tg3_dump_{eeprom, regs}() + * Fix: fix unused parameter warning in vioc_dump_regs() + * Fix: fix unused parameter warning in e100_dump_regs() + * Fix: fix unused parameter warning in de2104[01]_dump_regs() + * Fix: fix unused parameter warning in igb_dump_regs() + * Fix: fix unused parameter warning in e1000_dump_regs() + * Fix: fix unused parameter warning in smsc911x_dump_regs() + * Fix: fix unused parameter warning in at76c50x_usb_dump_regs() + * Fix: fix unused parameter warning in fec_dump_regs() + * Fix: fix unused parameter warning in amd8111e_dump_regs() + * Fix: fix unused parameter warning in et131x_dump_regs() + * Fix: fix unused parameter warning in ibm_emac_dump_regs() + * Fix: fix unused parameter warning in ixgb_dump_regs() + * Fix: fix unused parameter warning in fjes_dump_regs() + * Fix: fix unused parameter warning in e1000_get_mac_type() + * Fix: ethtool: correctly interpret bitrate of 255 + * Fix: ethtool: mark 10G Base-ER as SFF-8472 revision 10.4 onwards + * Fix: ethtool: add 0x16 and 0x1c extended compliance codes + Version 5.3 - September 23, 2019 * Feature: igb: dump RR2DCDELAY register * Feature: dump nested registers @@ -1,2 +1,2 @@ set noparent -file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking +file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking @@ -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); @@ -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(®, ®s->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%zx\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; +} @@ -0,0 +1,1041 @@ +/** + * Description: + * + * This module adds CMIS 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 <errno.h> +#include "internal.h" +#include "sff-common.h" +#include "cmis.h" +#include "netlink/extapi.h" + +/* The maximum number of supported Banks. Relevant documents: + * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40 + */ +#define CMIS_MAX_BANKS 4 +#define CMIS_CHANNELS_PER_BANK 8 +#define CMIS_MAX_CHANNEL_NUM (CMIS_MAX_BANKS * CMIS_CHANNELS_PER_BANK) + +/* We are not parsing further than Page 11h. */ +#define CMIS_MAX_PAGES 18 + +struct cmis_memory_map { + const __u8 *lower_memory; + const __u8 *upper_memory[CMIS_MAX_BANKS][CMIS_MAX_PAGES]; +#define page_00h upper_memory[0x0][0x0] +#define page_01h upper_memory[0x0][0x1] +#define page_02h upper_memory[0x0][0x2] +}; + +#define CMIS_PAGE_SIZE 0x80 +#define CMIS_I2C_ADDRESS 0x50 + +static struct { + const char *str; + int offset; + __u8 value; /* Alarm is on if (offset & value) != 0. */ +} cmis_aw_mod_flags[] = { + { "Module temperature high alarm", + CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HALARM_STATUS }, + { "Module temperature low alarm", + CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LALARM_STATUS }, + { "Module temperature high warning", + CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HWARN_STATUS }, + { "Module temperature low warning", + CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LWARN_STATUS }, + + { "Module voltage high alarm", + CMIS_VCC_AW_OFFSET, CMIS_VCC_HALARM_STATUS }, + { "Module voltage low alarm", + CMIS_VCC_AW_OFFSET, CMIS_VCC_LALARM_STATUS }, + { "Module voltage high warning", + CMIS_VCC_AW_OFFSET, CMIS_VCC_HWARN_STATUS }, + { "Module voltage low warning", + CMIS_VCC_AW_OFFSET, CMIS_VCC_LWARN_STATUS }, + + { NULL, 0, 0 }, +}; + +static struct { + const char *fmt_str; + int offset; + int adver_offset; /* In Page 01h. */ + __u8 adver_value; /* Supported if (offset & value) != 0. */ +} cmis_aw_chan_flags[] = { + { "Laser bias current high alarm (Chan %d)", + CMIS_TX_BIAS_AW_HALARM_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, + { "Laser bias current low alarm (Chan %d)", + CMIS_TX_BIAS_AW_LALARM_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, + { "Laser bias current high warning (Chan %d)", + CMIS_TX_BIAS_AW_HWARN_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, + { "Laser bias current low warning (Chan %d)", + CMIS_TX_BIAS_AW_LWARN_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, + + { "Laser tx power high alarm (Channel %d)", + CMIS_TX_PWR_AW_HALARM_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, + { "Laser tx power low alarm (Channel %d)", + CMIS_TX_PWR_AW_LALARM_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, + { "Laser tx power high warning (Channel %d)", + CMIS_TX_PWR_AW_HWARN_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, + { "Laser tx power low warning (Channel %d)", + CMIS_TX_PWR_AW_LWARN_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, + + { "Laser rx power high alarm (Channel %d)", + CMIS_RX_PWR_AW_HALARM_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, + { "Laser rx power low alarm (Channel %d)", + CMIS_RX_PWR_AW_LALARM_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, + { "Laser rx power high warning (Channel %d)", + CMIS_RX_PWR_AW_HWARN_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, + { "Laser rx power low warning (Channel %d)", + CMIS_RX_PWR_AW_LWARN_OFFSET, + CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, + + { NULL, 0, 0, 0 }, +}; + +static void cmis_show_identifier(const struct cmis_memory_map *map) +{ + sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET); +} + +static void cmis_show_connector(const struct cmis_memory_map *map) +{ + sff8024_show_connector(map->page_00h, CMIS_CTOR_OFFSET); +} + +static void cmis_show_oui(const struct cmis_memory_map *map) +{ + sff8024_show_oui(map->page_00h, CMIS_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 cmis_show_rev_compliance(const struct cmis_memory_map *map) +{ + __u8 rev = map->lower_memory[CMIS_REV_COMPLIANCE_OFFSET]; + int major = (rev >> 4) & 0x0F; + int minor = rev & 0x0F; + + printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor); +} + +static void +cmis_show_signals_one(const struct cmis_memory_map *map, const char *name, + int off, int ioff, unsigned int imask) +{ + unsigned int v; + int i; + + if (!map->page_01h) + return; + + v = 0; + for (i = 0; i < CMIS_MAX_BANKS && map->upper_memory[i][0x11]; i++) + v |= map->upper_memory[i][0x11][off] << (i * 8); + + if (map->page_01h[ioff] & imask) + sff_show_lane_status(name, i * 8, "Yes", "No", v); +} + +static void cmis_show_signals(const struct cmis_memory_map *map) +{ + cmis_show_signals_one(map, "Rx loss of signal", CMIS_RX_LOS_OFFSET, + CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOS); + cmis_show_signals_one(map, "Tx loss of signal", CMIS_TX_LOS_OFFSET, + CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOS); + + cmis_show_signals_one(map, "Rx loss of lock", CMIS_RX_LOL_OFFSET, + CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOL); + cmis_show_signals_one(map, "Tx loss of lock", CMIS_TX_LOL_OFFSET, + CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOL); + + cmis_show_signals_one(map, "Tx fault", CMIS_TX_FAIL_OFFSET, + CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_FAIL); + + cmis_show_signals_one(map, "Tx adaptive eq fault", + CMIS_TX_EQ_FAIL_OFFSET, CMIS_DIAG_FLAGS_TX_OFFSET, + CMIS_DIAG_FL_TX_ADAPTIVE_EQ_FAIL); +} + +/** + * 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 cmis_show_power_info(const struct cmis_memory_map *map) +{ + float max_power = 0.0f; + __u8 base_power = 0; + __u8 power_class; + + /* Get the power class (first 3 most significat bytes) */ + power_class = (map->page_00h[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07; + + /* Get the base power in multiples of 0.25W */ + base_power = map->page_00h[CMIS_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 cmis_show_cbl_asm_len(const struct cmis_memory_map *map) +{ + static const char *fn = "Cable assembly length"; + float mul = 1.0f; + float val = 0.0f; + + /* Check if max length */ + if (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) { + printf("\t%-41s : > 6.3km\n", fn); + return; + } + + /* Get the multiplier from the first two bits */ + switch (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { + case CMIS_MULTIPLIER_00: + mul = 0.1f; + break; + case CMIS_MULTIPLIER_01: + mul = 1.0f; + break; + case CMIS_MULTIPLIER_10: + mul = 10.0f; + break; + case CMIS_MULTIPLIER_11: + mul = 100.0f; + break; + default: + break; + } + + /* Get base value from first 6 bits and multiply by mul */ + val = (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_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 cmis_print_smf_cbl_len(const struct cmis_memory_map *map) +{ + static const char *fn = "Length (SMF)"; + float mul = 1.0f; + float val = 0.0f; + + if (!map->page_01h) + return; + + /* Get the multiplier from the first two bits */ + switch (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { + case CMIS_MULTIPLIER_00: + mul = 0.1f; + break; + case CMIS_MULTIPLIER_01: + mul = 1.0f; + break; + default: + break; + } + + /* Get base value from first 6 bits and multiply by mul */ + val = (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_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 cmis_show_sig_integrity(const struct cmis_memory_map *map) +{ + if (!map->page_01h) + return; + + /* CDR Bypass control: 2nd bit from each byte */ + printf("\t%-41s : ", "Tx CDR bypass control"); + printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x02)); + + printf("\t%-41s : ", "Rx CDR bypass control"); + printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x02)); + + /* CDR Implementation: 1st bit from each byte */ + printf("\t%-41s : ", "Tx CDR"); + printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x01)); + + printf("\t%-41s : ", "Rx CDR"); + printf("%s\n", YESNO(map->page_01h[CMIS_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 cmis_show_mit_compliance(const struct cmis_memory_map *map) +{ + static const char *cc = " (Copper cable,"; + + printf("\t%-41s : 0x%02x", "Transmitter technology", + map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]); + + switch (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]) { + case CMIS_850_VCSEL: + printf(" (850 nm VCSEL)\n"); + break; + case CMIS_1310_VCSEL: + printf(" (1310 nm VCSEL)\n"); + break; + case CMIS_1550_VCSEL: + printf(" (1550 nm VCSEL)\n"); + break; + case CMIS_1310_FP: + printf(" (1310 nm FP)\n"); + break; + case CMIS_1310_DFB: + printf(" (1310 nm DFB)\n"); + break; + case CMIS_1550_DFB: + printf(" (1550 nm DFB)\n"); + break; + case CMIS_1310_EML: + printf(" (1310 nm EML)\n"); + break; + case CMIS_1550_EML: + printf(" (1550 nm EML)\n"); + break; + case CMIS_OTHERS: + printf(" (Others/Undefined)\n"); + break; + case CMIS_1490_DFB: + printf(" (1490 nm DFB)\n"); + break; + case CMIS_COPPER_UNEQUAL: + printf("%s unequalized)\n", cc); + break; + case CMIS_COPPER_PASS_EQUAL: + printf("%s passive equalized)\n", cc); + break; + case CMIS_COPPER_NF_EQUAL: + printf("%s near and far end limiting active equalizers)\n", cc); + break; + case CMIS_COPPER_F_EQUAL: + printf("%s far end limiting active equalizers)\n", cc); + break; + case CMIS_COPPER_N_EQUAL: + printf("%s near end limiting active equalizers)\n", cc); + break; + case CMIS_COPPER_LINEAR_EQUAL: + printf("%s linear active equalizers)\n", cc); + break; + } + + if (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) { + printf("\t%-41s : %udb\n", "Attenuation at 5GHz", + map->page_00h[CMIS_COPPER_ATT_5GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 7GHz", + map->page_00h[CMIS_COPPER_ATT_7GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz", + map->page_00h[CMIS_COPPER_ATT_12P9GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz", + map->page_00h[CMIS_COPPER_ATT_25P8GHZ]); + } else if (map->page_01h) { + printf("\t%-41s : %.3lfnm\n", "Laser wavelength", + (((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) | + map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05)); + printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance", + (((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) | + map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005)); + } +} + +/** + * 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 cmis_show_link_len(const struct cmis_memory_map *map) +{ + cmis_print_smf_cbl_len(map); + if (!map->page_01h) + return; + sff_show_value_with_unit(map->page_01h, CMIS_OM5_LEN_OFFSET, + "Length (OM5)", 2, "m"); + sff_show_value_with_unit(map->page_01h, CMIS_OM4_LEN_OFFSET, + "Length (OM4)", 2, "m"); + sff_show_value_with_unit(map->page_01h, CMIS_OM3_LEN_OFFSET, + "Length (OM3 50/125um)", 2, "m"); + sff_show_value_with_unit(map->page_01h, CMIS_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 cmis_show_vendor_info(const struct cmis_memory_map *map) +{ + const char *clei; + + sff_show_ascii(map->page_00h, CMIS_VENDOR_NAME_START_OFFSET, + CMIS_VENDOR_NAME_END_OFFSET, "Vendor name"); + cmis_show_oui(map); + sff_show_ascii(map->page_00h, CMIS_VENDOR_PN_START_OFFSET, + CMIS_VENDOR_PN_END_OFFSET, "Vendor PN"); + sff_show_ascii(map->page_00h, CMIS_VENDOR_REV_START_OFFSET, + CMIS_VENDOR_REV_END_OFFSET, "Vendor rev"); + sff_show_ascii(map->page_00h, CMIS_VENDOR_SN_START_OFFSET, + CMIS_VENDOR_SN_END_OFFSET, "Vendor SN"); + sff_show_ascii(map->page_00h, CMIS_DATE_YEAR_OFFSET, + CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); + + clei = (const char *)(map->page_00h + CMIS_CLEI_START_OFFSET); + if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN)) + sff_show_ascii(map->page_00h, CMIS_CLEI_START_OFFSET, + CMIS_CLEI_END_OFFSET, "CLEI code"); +} + +/* Print the current Module State. Relevant documents: + * [1] CMIS Rev. 5, pag. 57, section 6.3.2.2, Figure 6-3 + * [2] CMIS Rev. 5, pag. 60, section 6.3.2.3, Figure 6-4 + * [3] CMIS Rev. 5, pag. 107, section 8.2.2, Table 8-6 + */ +static void cmis_show_mod_state(const struct cmis_memory_map *map) +{ + __u8 mod_state; + + mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] & + CMIS_MODULE_STATE_MASK) >> 1; + printf("\t%-41s : 0x%02x", "Module State", mod_state); + switch (mod_state) { + case CMIS_MODULE_STATE_MODULE_LOW_PWR: + printf(" (ModuleLowPwr)\n"); + break; + case CMIS_MODULE_STATE_MODULE_PWR_UP: + printf(" (ModulePwrUp)\n"); + break; + case CMIS_MODULE_STATE_MODULE_READY: + printf(" (ModuleReady)\n"); + break; + case CMIS_MODULE_STATE_MODULE_PWR_DN: + printf(" (ModulePwrDn)\n"); + break; + case CMIS_MODULE_STATE_MODULE_FAULT: + printf(" (ModuleFault)\n"); + break; + default: + printf(" (reserved or unknown)\n"); + break; + } +} + +/* Print the Module Fault Information. Relevant documents: + * [1] CMIS Rev. 5, pag. 64, section 6.3.2.12 + * [2] CMIS Rev. 5, pag. 115, section 8.2.10, Table 8-15 + */ +static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map) +{ + __u8 mod_state, fault_cause; + + mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] & + CMIS_MODULE_STATE_MASK) >> 1; + if (mod_state != CMIS_MODULE_STATE_MODULE_FAULT) + return; + + fault_cause = map->lower_memory[CMIS_MODULE_FAULT_OFFSET]; + printf("\t%-41s : 0x%02x", "Module Fault Cause", fault_cause); + switch (fault_cause) { + case CMIS_MODULE_FAULT_NO_FAULT: + printf(" (No fault detected / not supported)\n"); + break; + case CMIS_MODULE_FAULT_TEC_RUNAWAY: + printf(" (TEC runaway)\n"); + break; + case CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED: + printf(" (Data memory corrupted)\n"); + break; + case CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED: + printf(" (Program memory corrupted)\n"); + break; + default: + printf(" (reserved or unknown)\n"); + break; + } +} + +/* Print the current Module-Level Controls. Relevant documents: + * [1] CMIS Rev. 5, pag. 58, section 6.3.2.2, Table 6-12 + * [2] CMIS Rev. 5, pag. 111, section 8.2.6, Table 8-10 + */ +static void cmis_show_mod_lvl_controls(const struct cmis_memory_map *map) +{ + printf("\t%-41s : ", "LowPwrAllowRequestHW"); + printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] & + CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK)); + printf("\t%-41s : ", "LowPwrRequestSW"); + printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] & + CMIS_LOW_PWR_REQUEST_SW_MASK)); +} + +static void cmis_parse_dom_power_type(const struct cmis_memory_map *map, + struct sff_diags *sd) +{ + sd->rx_power_type = map->page_01h[CMIS_DIAG_TYPE_OFFSET] & + CMIS_RX_PWR_TYPE_MASK; + sd->tx_power_type = map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & + CMIS_TX_PWR_MON_MASK; +} + +static void cmis_parse_dom_mod_lvl_monitors(const struct cmis_memory_map *map, + struct sff_diags *sd) +{ + sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(map->lower_memory, + CMIS_CURR_VCC_OFFSET); + sd->sfp_temp[MCURR] = (__s16)OFFSET_TO_U16_PTR(map->lower_memory, + CMIS_CURR_TEMP_OFFSET); +} + +static void cmis_parse_dom_mod_lvl_thresh(const struct cmis_memory_map *map, + struct sff_diags *sd) +{ + /* Page is not present in IOCTL path. */ + if (!map->page_02h) + return; + sd->supports_alarms = 1; + + sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_VCC_HALRM_OFFSET); + sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_VCC_LALRM_OFFSET); + sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_VCC_HWARN_OFFSET); + sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_VCC_LWARN_OFFSET); + + sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TEMP_HALRM_OFFSET); + sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TEMP_LALRM_OFFSET); + sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TEMP_HWARN_OFFSET); + sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TEMP_LWARN_OFFSET); +} + +static __u8 cmis_tx_bias_mul(const struct cmis_memory_map *map) +{ + switch (map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & + CMIS_TX_BIAS_MUL_MASK) { + case CMIS_TX_BIAS_MUL_1: + return 0; + case CMIS_TX_BIAS_MUL_2: + return 1; + case CMIS_TX_BIAS_MUL_4: + return 2; + } + + return 0; +} + +static void +cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map *map, + struct sff_diags *sd, int bank) +{ + const __u8 *page_11h = map->upper_memory[bank][0x11]; + int i; + + if (!page_11h) + return; + + for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { + __u8 tx_bias_offset, rx_power_offset, tx_power_offset; + int chan = bank * CMIS_CHANNELS_PER_BANK + i; + __u8 bias_mul = cmis_tx_bias_mul(map); + + tx_bias_offset = CMIS_TX_BIAS_OFFSET + i * sizeof(__u16); + rx_power_offset = CMIS_RX_PWR_OFFSET + i * sizeof(__u16); + tx_power_offset = CMIS_TX_PWR_OFFSET + i * sizeof(__u16); + + sd->scd[chan].bias_cur = OFFSET_TO_U16_PTR(page_11h, + tx_bias_offset); + sd->scd[chan].bias_cur >>= bias_mul; + sd->scd[chan].rx_power = OFFSET_TO_U16_PTR(page_11h, + rx_power_offset); + sd->scd[chan].tx_power = OFFSET_TO_U16_PTR(page_11h, + tx_power_offset); + } +} + +static void cmis_parse_dom_chan_lvl_monitors(const struct cmis_memory_map *map, + struct sff_diags *sd) +{ + int i; + + for (i = 0; i < CMIS_MAX_BANKS; i++) + cmis_parse_dom_chan_lvl_monitors_bank(map, sd, i); +} + +static void cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map *map, + struct sff_diags *sd) +{ + __u8 bias_mul = cmis_tx_bias_mul(map); + + if (!map->page_02h) + return; + + sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_BIAS_HALRM_OFFSET); + sd->bias_cur[HALRM] >>= bias_mul; + sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_BIAS_LALRM_OFFSET); + sd->bias_cur[LALRM] >>= bias_mul; + sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_BIAS_HWARN_OFFSET); + sd->bias_cur[HWARN] >>= bias_mul; + sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_BIAS_LWARN_OFFSET); + sd->bias_cur[LWARN] >>= bias_mul; + + sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_PWR_HALRM_OFFSET); + sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_PWR_LALRM_OFFSET); + sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_PWR_HWARN_OFFSET); + sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_TX_PWR_LWARN_OFFSET); + + sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_RX_PWR_HALRM_OFFSET); + sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_RX_PWR_LALRM_OFFSET); + sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_RX_PWR_HWARN_OFFSET); + sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, + CMIS_RX_PWR_LWARN_OFFSET); +} + +static void cmis_parse_dom(const struct cmis_memory_map *map, + struct sff_diags *sd) +{ + cmis_parse_dom_power_type(map, sd); + cmis_parse_dom_mod_lvl_monitors(map, sd); + cmis_parse_dom_mod_lvl_thresh(map, sd); + cmis_parse_dom_chan_lvl_monitors(map, sd); + cmis_parse_dom_chan_lvl_thresh(map, sd); +} + +/* Print module-level monitoring values. Relevant documents: + * [1] CMIS Rev. 5, page 110, section 8.2.5, Table 8-9 + */ +static void cmis_show_dom_mod_lvl_monitors(const struct sff_diags *sd) +{ + PRINT_TEMP("Module temperature", sd->sfp_temp[MCURR]); + PRINT_VCC("Module voltage", sd->sfp_voltage[MCURR]); +} + +/* Print channel Tx laser bias current. Relevant documents: + * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 + */ +static void +cmis_show_dom_chan_lvl_tx_bias_bank(const struct cmis_memory_map *map, + const struct sff_diags *sd, int bank) +{ + const __u8 *page_11h = map->upper_memory[bank][0x11]; + int i; + + if (!page_11h) + return; + + for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { + int chan = bank * CMIS_CHANNELS_PER_BANK + i; + char fmt_str[80]; + + snprintf(fmt_str, 80, "%s (Channel %d)", + "Laser tx bias current", chan + 1); + PRINT_BIAS(fmt_str, sd->scd[chan].bias_cur); + } +} + +static void cmis_show_dom_chan_lvl_tx_bias(const struct cmis_memory_map *map, + const struct sff_diags *sd) +{ + int i; + + if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & + CMIS_TX_BIAS_MON_MASK)) + return; + + for (i = 0; i < CMIS_MAX_BANKS; i++) + cmis_show_dom_chan_lvl_tx_bias_bank(map, sd, i); +} + +/* Print channel Tx average optical power. Relevant documents: + * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 + */ +static void +cmis_show_dom_chan_lvl_tx_power_bank(const struct cmis_memory_map *map, + const struct sff_diags *sd, int bank) +{ + const __u8 *page_11h = map->upper_memory[bank][0x11]; + int i; + + if (!page_11h) + return; + + for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { + int chan = bank * CMIS_CHANNELS_PER_BANK + i; + char fmt_str[80]; + + snprintf(fmt_str, 80, "%s (Channel %d)", + "Transmit avg optical power", chan + 1); + PRINT_xX_PWR(fmt_str, sd->scd[chan].tx_power); + } +} + +static void cmis_show_dom_chan_lvl_tx_power(const struct cmis_memory_map *map, + const struct sff_diags *sd) +{ + int i; + + if (!sd->tx_power_type) + return; + + for (i = 0; i < CMIS_MAX_BANKS; i++) + cmis_show_dom_chan_lvl_tx_power_bank(map, sd, i); +} + +/* Print channel Rx input optical power. Relevant documents: + * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 + */ +static void +cmis_show_dom_chan_lvl_rx_power_bank(const struct cmis_memory_map *map, + const struct sff_diags *sd, int bank) +{ + const __u8 *page_11h = map->upper_memory[bank][0x11]; + int i; + + if (!page_11h) + return; + + for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { + int chan = bank * CMIS_CHANNELS_PER_BANK + i; + char *rx_power_str; + char fmt_str[80]; + + if (!sd->rx_power_type) + rx_power_str = "Receiver signal OMA"; + else + rx_power_str = "Rcvr signal avg optical power"; + + snprintf(fmt_str, 80, "%s (Channel %d)", rx_power_str, + chan + 1); + PRINT_xX_PWR(fmt_str, sd->scd[chan].rx_power); + } +} + +static void cmis_show_dom_chan_lvl_rx_power(const struct cmis_memory_map *map, + const struct sff_diags *sd) +{ + int i; + + if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & CMIS_RX_PWR_MON_MASK)) + return; + + for (i = 0; i < CMIS_MAX_BANKS; i++) + cmis_show_dom_chan_lvl_rx_power_bank(map, sd, i); +} + +static void cmis_show_dom_chan_lvl_monitors(const struct cmis_memory_map *map, + const struct sff_diags *sd) +{ + cmis_show_dom_chan_lvl_tx_bias(map, sd); + cmis_show_dom_chan_lvl_tx_power(map, sd); + cmis_show_dom_chan_lvl_rx_power(map, sd); +} + +/* Print module-level flags. Relevant documents: + * [1] CMIS Rev. 5, page 109, section 8.2.4, Table 8-8 + */ +static void cmis_show_dom_mod_lvl_flags(const struct cmis_memory_map *map) +{ + int i; + + for (i = 0; cmis_aw_mod_flags[i].str; i++) { + printf("\t%-41s : %s\n", cmis_aw_mod_flags[i].str, + map->lower_memory[cmis_aw_mod_flags[i].offset] & + cmis_aw_mod_flags[i].value ? "On" : "Off"); + } +} + +/* Print channel-level flags. Relevant documents: + * [1] CMIS Rev. 5, page 162, section 8.9.3, Table 8-77 + * [1] CMIS Rev. 5, page 164, section 8.9.3, Table 8-78 + */ +static void cmis_show_dom_chan_lvl_flags_chan(const struct cmis_memory_map *map, + int bank, int chan) +{ + const __u8 *page_11h = map->upper_memory[bank][0x11]; + int i; + + for (i = 0; cmis_aw_chan_flags[i].fmt_str; i++) { + char str[80]; + + if (!(map->page_01h[cmis_aw_chan_flags[i].adver_offset] & + cmis_aw_chan_flags[i].adver_value)) + continue; + + snprintf(str, 80, cmis_aw_chan_flags[i].fmt_str, chan + 1); + printf("\t%-41s : %s\n", str, + page_11h[cmis_aw_chan_flags[i].offset] & chan ? + "On" : "Off"); + } +} + +static void +cmis_show_dom_chan_lvl_flags_bank(const struct cmis_memory_map *map, + int bank) +{ + const __u8 *page_11h = map->upper_memory[bank][0x11]; + int i; + + if (!page_11h) + return; + + for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { + int chan = bank * CMIS_CHANNELS_PER_BANK + i; + + cmis_show_dom_chan_lvl_flags_chan(map, bank, chan); + } +} + +static void cmis_show_dom_chan_lvl_flags(const struct cmis_memory_map *map) +{ + int i; + + for (i = 0; i < CMIS_MAX_BANKS; i++) + cmis_show_dom_chan_lvl_flags_bank(map, i); +} + + +static void cmis_show_dom(const struct cmis_memory_map *map) +{ + struct sff_diags sd = {}; + + /* Diagnostic information is only relevant when the module memory + * model is paged and not flat. + */ + if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & + CMIS_MEMORY_MODEL_MASK) + return; + + cmis_parse_dom(map, &sd); + + cmis_show_dom_mod_lvl_monitors(&sd); + cmis_show_dom_chan_lvl_monitors(map, &sd); + cmis_show_dom_mod_lvl_flags(map); + cmis_show_dom_chan_lvl_flags(map); + if (sd.supports_alarms) + sff_show_thresholds(sd); +} + +static void cmis_show_all_common(const struct cmis_memory_map *map) +{ + cmis_show_identifier(map); + cmis_show_power_info(map); + cmis_show_connector(map); + cmis_show_cbl_asm_len(map); + cmis_show_sig_integrity(map); + cmis_show_mit_compliance(map); + cmis_show_link_len(map); + cmis_show_vendor_info(map); + cmis_show_rev_compliance(map); + cmis_show_signals(map); + cmis_show_mod_state(map); + cmis_show_mod_fault_cause(map); + cmis_show_mod_lvl_controls(map); + cmis_show_dom(map); +} + +static void cmis_memory_map_init_buf(struct cmis_memory_map *map, + const __u8 *id) +{ + /* Lower Memory and Page 00h are always present. + * + * Offset into Upper Memory is between page size and twice the page + * size. Therefore, set the base address of each page to base address + * plus page size multiplied by the page number. + */ + map->lower_memory = id; + map->page_00h = id; + + /* Page 01h is only present when the module memory model is paged and + * not flat. + */ + if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & + CMIS_MEMORY_MODEL_MASK) + return; + + map->page_01h = id + CMIS_PAGE_SIZE; +} + +void cmis_show_all_ioctl(const __u8 *id) +{ + struct cmis_memory_map map = {}; + + cmis_memory_map_init_buf(&map, id); + cmis_show_all_common(&map); +} + +static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank, + u8 page, u32 offset) +{ + request->offset = offset; + request->length = CMIS_PAGE_SIZE; + request->page = page; + request->bank = bank; + request->i2c_address = CMIS_I2C_ADDRESS; + request->data = NULL; +} + +static int cmis_num_banks_get(const struct cmis_memory_map *map, + int *p_num_banks) +{ + switch (map->page_01h[CMIS_PAGES_ADVER_OFFSET] & + CMIS_BANKS_SUPPORTED_MASK) { + case CMIS_BANK_0_SUPPORTED: + *p_num_banks = 1; + break; + case CMIS_BANK_0_1_SUPPORTED: + *p_num_banks = 2; + break; + case CMIS_BANK_0_3_SUPPORTED: + *p_num_banks = 4; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +cmis_memory_map_init_pages(struct cmd_context *ctx, + struct cmis_memory_map *map) +{ + struct ethtool_module_eeprom request; + int num_banks, i, ret; + + /* Lower Memory and Page 00h are always present. + * + * Offset into Upper Memory is between page size and twice the page + * size. Therefore, set the base address of each page to its base + * address minus page size. + */ + cmis_request_init(&request, 0, 0x0, 0); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->lower_memory = request.data; + + cmis_request_init(&request, 0, 0x0, CMIS_PAGE_SIZE); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->page_00h = request.data - CMIS_PAGE_SIZE; + + /* Pages 01h and 02h are only present when the module memory model is + * paged and not flat. + */ + if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & + CMIS_MEMORY_MODEL_MASK) + return 0; + + cmis_request_init(&request, 0, 0x1, CMIS_PAGE_SIZE); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->page_01h = request.data - CMIS_PAGE_SIZE; + + cmis_request_init(&request, 0, 0x2, CMIS_PAGE_SIZE); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->page_02h = request.data - CMIS_PAGE_SIZE; + + /* Bank 0 of Page 11h provides lane-specific registers for the first 8 + * lanes, and each additional Banks provides support for an additional + * 8 lanes. Only initialize supported Banks. + */ + ret = cmis_num_banks_get(map, &num_banks); + if (ret < 0) + return ret; + + for (i = 0; i < num_banks; i++) { + cmis_request_init(&request, i, 0x11, CMIS_PAGE_SIZE); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->upper_memory[i][0x11] = request.data - CMIS_PAGE_SIZE; + } + + return 0; +} + +int cmis_show_all_nl(struct cmd_context *ctx) +{ + struct cmis_memory_map map = {}; + int ret; + + ret = cmis_memory_map_init_pages(ctx, &map); + if (ret < 0) + return ret; + cmis_show_all_common(&map); + + return 0; +} @@ -0,0 +1,252 @@ +#ifndef CMIS_H__ +#define CMIS_H__ + +/* Identifier and revision compliance (Page 0) */ +#define CMIS_ID_OFFSET 0x00 +#define CMIS_REV_COMPLIANCE_OFFSET 0x01 +#define CMIS_MEMORY_MODEL_OFFSET 0x02 +#define CMIS_MEMORY_MODEL_MASK 0x80 + +/* Global Status Information (Page 0) */ +#define CMIS_MODULE_STATE_OFFSET 0x03 +#define CMIS_MODULE_STATE_MASK 0x0E +#define CMIS_MODULE_STATE_MODULE_LOW_PWR 0x01 +#define CMIS_MODULE_STATE_MODULE_PWR_UP 0x02 +#define CMIS_MODULE_STATE_MODULE_READY 0x03 +#define CMIS_MODULE_STATE_MODULE_PWR_DN 0x04 +#define CMIS_MODULE_STATE_MODULE_FAULT 0x05 + +/* Module Flags (Page 0) */ +#define CMIS_VCC_AW_OFFSET 0x09 +#define CMIS_VCC_LWARN_STATUS 0x80 +#define CMIS_VCC_HWARN_STATUS 0x40 +#define CMIS_VCC_LALARM_STATUS 0x20 +#define CMIS_VCC_HALARM_STATUS 0x10 +#define CMIS_TEMP_AW_OFFSET 0x09 +#define CMIS_TEMP_LWARN_STATUS 0x08 +#define CMIS_TEMP_HWARN_STATUS 0x04 +#define CMIS_TEMP_LALARM_STATUS 0x02 +#define CMIS_TEMP_HALARM_STATUS 0x01 + +#define CMIS_MODULE_TYPE_OFFSET 0x55 +#define CMIS_MT_MMF 0x01 +#define CMIS_MT_SMF 0x02 + +/* Module-Level Monitors (Page 0) */ +#define CMIS_CURR_TEMP_OFFSET 0x0E +#define CMIS_CURR_VCC_OFFSET 0x10 + +/* Module Global Controls (Page 0) */ +#define CMIS_MODULE_CONTROL_OFFSET 0x1A +#define CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK 0x40 +#define CMIS_LOW_PWR_REQUEST_SW_MASK 0x10 + +/* Module Fault Information (Page 0) */ +#define CMIS_MODULE_FAULT_OFFSET 0x29 +#define CMIS_MODULE_FAULT_NO_FAULT 0x00 +#define CMIS_MODULE_FAULT_TEC_RUNAWAY 0x01 +#define CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED 0x02 +#define CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED 0x03 + +#define CMIS_CTOR_OFFSET 0xCB + +/* Vendor related information (Page 0) */ +#define CMIS_VENDOR_NAME_START_OFFSET 0x81 +#define CMIS_VENDOR_NAME_END_OFFSET 0x90 + +#define CMIS_VENDOR_OUI_OFFSET 0x91 + +#define CMIS_VENDOR_PN_START_OFFSET 0x94 +#define CMIS_VENDOR_PN_END_OFFSET 0xA3 + +#define CMIS_VENDOR_REV_START_OFFSET 0xA4 +#define CMIS_VENDOR_REV_END_OFFSET 0xA5 + +#define CMIS_VENDOR_SN_START_OFFSET 0xA6 +#define CMIS_VENDOR_SN_END_OFFSET 0xB5 + +#define CMIS_DATE_YEAR_OFFSET 0xB6 +#define CMIS_DATE_VENDOR_LOT_OFFSET 0xBC + +/* CLEI Code (Page 0) */ +#define CMIS_CLEI_START_OFFSET 0xBE +#define CMIS_CLEI_END_OFFSET 0xC7 +#define CMIS_CLEI_BLANK " " +#define CMIS_CLEI_LEN 0x0A + +/* Cable assembly length */ +#define CMIS_CBL_ASM_LEN_OFFSET 0xCA +#define CMIS_6300M_MAX_LEN 0xFF + +/* Cable length with multiplier */ +#define CMIS_MULTIPLIER_00 0x00 +#define CMIS_MULTIPLIER_01 0x40 +#define CMIS_MULTIPLIER_10 0x80 +#define CMIS_MULTIPLIER_11 0xC0 +#define CMIS_LEN_MUL_MASK 0xC0 +#define CMIS_LEN_VAL_MASK 0x3F + +/* Module power characteristics */ +#define CMIS_PWR_CLASS_OFFSET 0xC8 +#define CMIS_PWR_MAX_POWER_OFFSET 0xC9 +#define CMIS_PWR_CLASS_MASK 0xE0 +#define CMIS_PWR_CLASS_1 0x00 +#define CMIS_PWR_CLASS_2 0x01 +#define CMIS_PWR_CLASS_3 0x02 +#define CMIS_PWR_CLASS_4 0x03 +#define CMIS_PWR_CLASS_5 0x04 +#define CMIS_PWR_CLASS_6 0x05 +#define CMIS_PWR_CLASS_7 0x06 +#define CMIS_PWR_CLASS_8 0x07 + +/* Copper cable attenuation */ +#define CMIS_COPPER_ATT_5GHZ 0xCC +#define CMIS_COPPER_ATT_7GHZ 0xCD +#define CMIS_COPPER_ATT_12P9GHZ 0xCE +#define CMIS_COPPER_ATT_25P8GHZ 0xCF + +/* Cable assembly lane */ +#define CMIS_CABLE_ASM_NEAR_END_OFFSET 0xD2 +#define CMIS_CABLE_ASM_FAR_END_OFFSET 0xD3 + +/* Media interface technology */ +#define CMIS_MEDIA_INTF_TECH_OFFSET 0xD4 +#define CMIS_850_VCSEL 0x00 +#define CMIS_1310_VCSEL 0x01 +#define CMIS_1550_VCSEL 0x02 +#define CMIS_1310_FP 0x03 +#define CMIS_1310_DFB 0x04 +#define CMIS_1550_DFB 0x05 +#define CMIS_1310_EML 0x06 +#define CMIS_1550_EML 0x07 +#define CMIS_OTHERS 0x08 +#define CMIS_1490_DFB 0x09 +#define CMIS_COPPER_UNEQUAL 0x0A +#define CMIS_COPPER_PASS_EQUAL 0x0B +#define CMIS_COPPER_NF_EQUAL 0x0C +#define CMIS_COPPER_F_EQUAL 0x0D +#define CMIS_COPPER_N_EQUAL 0x0E +#define CMIS_COPPER_LINEAR_EQUAL 0x0F + +/*----------------------------------------------------------------------- + * Upper Memory Page 0x01: contains advertising fields that define properties + * that are unique to active modules and cable assemblies. + * GlobalOffset = 2 * 0x80 + LocalOffset + */ + +/* Supported Link Length (Page 1) */ +#define CMIS_SMF_LEN_OFFSET 0x84 +#define CMIS_OM5_LEN_OFFSET 0x85 +#define CMIS_OM4_LEN_OFFSET 0x86 +#define CMIS_OM3_LEN_OFFSET 0x87 +#define CMIS_OM2_LEN_OFFSET 0x88 + +/* Wavelength (Page 1) */ +#define CMIS_NOM_WAVELENGTH_MSB 0x8A +#define CMIS_NOM_WAVELENGTH_LSB 0x8B +#define CMIS_WAVELENGTH_TOL_MSB 0x8C +#define CMIS_WAVELENGTH_TOL_LSB 0x8D + +/* Supported Pages Advertising (Page 1) */ +#define CMIS_PAGES_ADVER_OFFSET 0x8E +#define CMIS_BANKS_SUPPORTED_MASK 0x03 +#define CMIS_BANK_0_SUPPORTED 0x00 +#define CMIS_BANK_0_1_SUPPORTED 0x01 +#define CMIS_BANK_0_3_SUPPORTED 0x02 + +/* Module Characteristics Advertising (Page 1) */ +#define CMIS_DIAG_TYPE_OFFSET 0x97 +#define CMIS_RX_PWR_TYPE_MASK 0x10 + +/* Supported Flags Advertisement (Page 1) */ +#define CMIS_DIAG_FLAGS_TX_OFFSET 0x9d +#define CMIS_DIAG_FL_TX_ADAPTIVE_EQ_FAIL (1 << 3) +#define CMIS_DIAG_FL_TX_LOL (1 << 2) +#define CMIS_DIAG_FL_TX_LOS (1 << 1) +#define CMIS_DIAG_FL_TX_FAIL (1 << 0) + +#define CMIS_DIAG_FLAGS_RX_OFFSET 0x9e +#define CMIS_DIAG_FL_RX_LOL (1 << 2) +#define CMIS_DIAG_FL_RX_LOS (1 << 1) + +/* Supported Monitors Advertisement (Page 1) */ +#define CMIS_DIAG_CHAN_ADVER_OFFSET 0xA0 +#define CMIS_TX_BIAS_MON_MASK 0x01 +#define CMIS_TX_PWR_MON_MASK 0x02 +#define CMIS_RX_PWR_MON_MASK 0x04 +#define CMIS_TX_BIAS_MUL_MASK 0x18 +#define CMIS_TX_BIAS_MUL_1 0x00 +#define CMIS_TX_BIAS_MUL_2 0x08 +#define CMIS_TX_BIAS_MUL_4 0x10 + +/* Signal integrity controls */ +#define CMIS_SIG_INTEG_TX_OFFSET 0xA1 +#define CMIS_SIG_INTEG_RX_OFFSET 0xA2 + +/*----------------------------------------------------------------------- + * Upper Memory Page 0x02: Optional Page that informs about module-defined + * thresholds for module-level and lane-specific threshold crossing monitors. + */ + +/* Module-Level Monitor Thresholds (Page 2) */ +#define CMIS_TEMP_HALRM_OFFSET 0x80 +#define CMIS_TEMP_LALRM_OFFSET 0x82 +#define CMIS_TEMP_HWARN_OFFSET 0x84 +#define CMIS_TEMP_LWARN_OFFSET 0x86 +#define CMIS_VCC_HALRM_OFFSET 0x88 +#define CMIS_VCC_LALRM_OFFSET 0x8A +#define CMIS_VCC_HWARN_OFFSET 0x8C +#define CMIS_VCC_LWARN_OFFSET 0x8E + +/* Lane-Related Monitor Thresholds (Page 2) */ +#define CMIS_TX_PWR_HALRM_OFFSET 0xB0 +#define CMIS_TX_PWR_LALRM_OFFSET 0xB2 +#define CMIS_TX_PWR_HWARN_OFFSET 0xB4 +#define CMIS_TX_PWR_LWARN_OFFSET 0xB6 +#define CMIS_TX_BIAS_HALRM_OFFSET 0xB8 +#define CMIS_TX_BIAS_LALRM_OFFSET 0xBA +#define CMIS_TX_BIAS_HWARN_OFFSET 0xBC +#define CMIS_TX_BIAS_LWARN_OFFSET 0xBE +#define CMIS_RX_PWR_HALRM_OFFSET 0xC0 +#define CMIS_RX_PWR_LALRM_OFFSET 0xC2 +#define CMIS_RX_PWR_HWARN_OFFSET 0xC4 +#define CMIS_RX_PWR_LWARN_OFFSET 0xC6 + +/*----------------------------------------------------------------------- + * Upper Memory Page 0x11: Optional Page that contains lane dynamic status + * bytes. + */ + +/* Media Lane-Specific Flags (Page 0x11) */ +#define CMIS_TX_FAIL_OFFSET 0x87 +#define CMIS_TX_LOS_OFFSET 0x88 +#define CMIS_TX_LOL_OFFSET 0x89 +#define CMIS_TX_EQ_FAIL_OFFSET 0x8a +#define CMIS_TX_PWR_AW_HALARM_OFFSET 0x8B +#define CMIS_TX_PWR_AW_LALARM_OFFSET 0x8C +#define CMIS_TX_PWR_AW_HWARN_OFFSET 0x8D +#define CMIS_TX_PWR_AW_LWARN_OFFSET 0x8E +#define CMIS_TX_BIAS_AW_HALARM_OFFSET 0x8F +#define CMIS_TX_BIAS_AW_LALARM_OFFSET 0x90 +#define CMIS_TX_BIAS_AW_HWARN_OFFSET 0x91 +#define CMIS_TX_BIAS_AW_LWARN_OFFSET 0x92 +#define CMIS_RX_LOS_OFFSET 0x93 +#define CMIS_RX_LOL_OFFSET 0x94 +#define CMIS_RX_PWR_AW_HALARM_OFFSET 0x95 +#define CMIS_RX_PWR_AW_LALARM_OFFSET 0x96 +#define CMIS_RX_PWR_AW_HWARN_OFFSET 0x97 +#define CMIS_RX_PWR_AW_LWARN_OFFSET 0x98 + +/* Media Lane-Specific Monitors (Page 0x11) */ +#define CMIS_TX_PWR_OFFSET 0x9A +#define CMIS_TX_BIAS_OFFSET 0xAA +#define CMIS_RX_PWR_OFFSET 0xBA + +#define YESNO(x) (((x) != 0) ? "Yes" : "No") +#define ONOFF(x) (((x) != 0) ? "On" : "Off") + +void cmis_show_all_ioctl(const __u8 *id); + +int cmis_show_all_nl(struct cmd_context *ctx); + +#endif /* CMIS_H__ */ diff --git a/common.c b/common.c new file mode 100644 index 0000000..b8fd4d5 --- /dev/null +++ b/common.c @@ -0,0 +1,211 @@ +/* + * common.h - common code header + * + * Data and functions shared by ioctl and netlink implementation. + */ + +#include "internal.h" +#include "common.h" + +#ifndef HAVE_NETIF_MSG +enum { + NETIF_MSG_DRV = 0x0001, + NETIF_MSG_PROBE = 0x0002, + NETIF_MSG_LINK = 0x0004, + NETIF_MSG_TIMER = 0x0008, + NETIF_MSG_IFDOWN = 0x0010, + NETIF_MSG_IFUP = 0x0020, + NETIF_MSG_RX_ERR = 0x0040, + NETIF_MSG_TX_ERR = 0x0080, + NETIF_MSG_TX_QUEUED = 0x0100, + NETIF_MSG_INTR = 0x0200, + NETIF_MSG_TX_DONE = 0x0400, + NETIF_MSG_RX_STATUS = 0x0800, + NETIF_MSG_PKTDATA = 0x1000, + NETIF_MSG_HW = 0x2000, + NETIF_MSG_WOL = 0x4000, +}; +#endif + +const struct flag_info flags_msglvl[] = { + { "drv", NETIF_MSG_DRV }, + { "probe", NETIF_MSG_PROBE }, + { "link", NETIF_MSG_LINK }, + { "timer", NETIF_MSG_TIMER }, + { "ifdown", NETIF_MSG_IFDOWN }, + { "ifup", NETIF_MSG_IFUP }, + { "rx_err", NETIF_MSG_RX_ERR }, + { "tx_err", NETIF_MSG_TX_ERR }, + { "tx_queued", NETIF_MSG_TX_QUEUED }, + { "intr", NETIF_MSG_INTR }, + { "tx_done", NETIF_MSG_TX_DONE }, + { "rx_status", NETIF_MSG_RX_STATUS }, + { "pktdata", NETIF_MSG_PKTDATA }, + { "hw", NETIF_MSG_HW }, + { "wol", NETIF_MSG_WOL }, + {} +}; +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 = ""; + + while (n_info) { + if (value & info->value) { + printf("%s%s", sep, info->name); + sep = " "; + value &= ~info->value; + } + ++info; + --n_info; + } + + /* Print any unrecognised flags in hex */ + if (value) + printf("%s%#x", sep, value); +} + +static char *unparse_wolopts(int wolopts) +{ + static char buf[16]; + char *p = buf; + + memset(buf, 0, sizeof(buf)); + + if (wolopts) { + if (wolopts & WAKE_PHY) + *p++ = 'p'; + if (wolopts & WAKE_UCAST) + *p++ = 'u'; + if (wolopts & WAKE_MCAST) + *p++ = 'm'; + if (wolopts & WAKE_BCAST) + *p++ = 'b'; + if (wolopts & WAKE_ARP) + *p++ = 'a'; + if (wolopts & WAKE_MAGIC) + *p++ = 'g'; + if (wolopts & WAKE_MAGICSECURE) + *p++ = 's'; + if (wolopts & WAKE_FILTER) + *p++ = 'f'; + } else { + *p = 'd'; + } + + return buf; +} + +int dump_wol(struct ethtool_wolinfo *wol) +{ + fprintf(stdout, " Supports Wake-on: %s\n", + unparse_wolopts(wol->supported)); + fprintf(stdout, " Wake-on: %s\n", + unparse_wolopts(wol->wolopts)); + if (wol->supported & WAKE_MAGICSECURE) { + int i; + int delim = 0; + + fprintf(stdout, " SecureOn password: "); + for (i = 0; i < SOPASS_MAX; i++) { + fprintf(stdout, "%s%02x", delim ? ":" : "", + wol->sopass[i]); + delim = 1; + } + fprintf(stdout, "\n"); + } + + return 0; +} + +void dump_mdix(u8 mdix, u8 mdix_ctrl) +{ + fprintf(stdout, " MDI-X: "); + if (mdix_ctrl == ETH_TP_MDI) { + fprintf(stdout, "off (forced)\n"); + } else if (mdix_ctrl == ETH_TP_MDI_X) { + fprintf(stdout, "on (forced)\n"); + } else { + switch (mdix) { + case ETH_TP_MDI: + fprintf(stdout, "off"); + break; + case ETH_TP_MDI_X: + fprintf(stdout, "on"); + break; + default: + fprintf(stdout, "Unknown"); + break; + } + if (mdix_ctrl == ETH_TP_MDI_AUTO) + fprintf(stdout, " (auto)"); + fprintf(stdout, "\n"); + } +} + +void print_indir_table(struct cmd_context *ctx, u64 ring_count, + u32 indir_size, u32 *indir) +{ + u32 i; + + printf("RX flow hash indirection table for %s with %llu RX ring(s):\n", + ctx->devname, ring_count); + + if (!indir_size) + printf("Operation not supported\n"); + + for (i = 0; i < indir_size; i++) { + if (i % 8 == 0) + printf("%5u: ", i); + printf(" %5u", indir[i]); + if (i % 8 == 7 || i == indir_size - 1) + fputc('\n', stdout); + } +} + +void print_rss_hkey(u8 *hkey, u32 hkey_size) +{ + u32 i; + + printf("RSS hash key:\n"); + if (!hkey_size || !hkey) + printf("Operation not supported\n"); + + for (i = 0; i < hkey_size; i++) { + if (i == (hkey_size - 1)) + printf("%02x\n", hkey[i]); + else + printf("%02x:", hkey[i]); + } +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..f975407 --- /dev/null +++ b/common.h @@ -0,0 +1,49 @@ +/* + * common.h - common code header + * + * Declarations for data and functions shared by ioctl and netlink code. + */ + +#ifndef ETHTOOL_COMMON_H__ +#define ETHTOOL_COMMON_H__ + +#include "internal.h" +#include <stddef.h> +#include <errno.h> + +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) + +struct flag_info { + const char *name; + u32 value; +}; + +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); +void print_indir_table(struct cmd_context *ctx, u64 ring_count, + u32 indir_size, u32 *indir); +void print_rss_hkey(u8 *hkey, u32 hkey_size); +#endif /* ETHTOOL_COMMON_H__ */ diff --git a/configure.ac b/configure.ac index 56e4683..11efb99 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,9 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(ethtool, 5.3, netdev@vger.kernel.org) +AC_INIT(ethtool, 6.5, netdev@vger.kernel.org) AC_PREREQ(2.52) +AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([ethtool.c]) -AM_INIT_AUTOMAKE([gnu]) +AM_INIT_AUTOMAKE([gnu subdir-objects]) AC_CONFIG_HEADERS([ethtool-config.h]) AM_MAINTAINER_MODE @@ -13,22 +14,25 @@ AC_PROG_GCC_TRADITIONAL AM_PROG_CC_C_O PKG_PROG_PKG_CONFIG +AC_DEFUN([AX_CHECK_STDC], + [AX_CHECK_COMPILE_FLAG([-std=gnu11], + [AX_APPEND_FLAG([-std=gnu11])], + [AX_CHECK_COMPILE_FLAG([-std=c11], + [AX_APPEND_FLAG([-std=c11])], + [AC_MSG_ERROR([$PACKAGE requires a C11 compiler])]) + ]) + ]) +AX_CHECK_STDC + dnl Checks for libraries. dnl Checks for header files. dnl Checks for typedefs, structures, and compiler characteristics. -AC_MSG_CHECKING([whether <linux/types.h> defines big-endian types]) -AC_TRY_COMPILE([#include <linux/types.h>], - [__be16 foo;__be32 bar;], - [AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_BE_TYPES], [1], - [Define to 1 if <linux/types.h> defines big-endian types])], - [AC_MSG_RESULT(no)]) dnl Checks for library functions. AC_HEADER_STDC -AC_CHECK_FUNCS(socket strtol) +AC_CHECK_FUNCS(socket) dnl Check for options AC_ARG_ENABLE(pretty-dump, @@ -66,5 +70,17 @@ AC_SUBST([BASH_COMPLETION_DIR]) AM_CONDITIONAL([ENABLE_BASH_COMPLETION], [test "x$with_bash_completion_dir" != xno]) +AC_ARG_ENABLE(netlink, + [ --enable-netlink enable netlink interface (enabled by default)], + , + enable_netlink=yes) +if test x$enable_netlink = xyes; then + PKG_PROG_PKG_CONFIG + PKG_CHECK_MODULES([MNL], [libmnl]) + AC_DEFINE(ETHTOOL_ENABLE_NETLINK, 1, + Define this to support netlink interface to talk to kernel.) +fi +AM_CONDITIONAL([ETHTOOL_ENABLE_NETLINK], [test x$enable_netlink = xyes]) + AC_CONFIG_FILES([Makefile ethtool.spec ethtool.8]) AC_OUTPUT @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Code to dump registers for TI CPSW switch devices. + * + * Copyright (c) 2022 Linutronix GmbH + * Author: Benedikt Spranger <b.spranger@linutronix.de> + */ + +#include <stdio.h> +#include <string.h> + +#include "internal.h" + +#define ALE_ENTRY_BITS 68 +#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32) +#define ALE_ENTRY_BYTES (ALE_ENTRY_WORDS * 4) + +struct address_table_entry +{ + u8 port; + u8 reserved1; + u8 reserved2; + u8 reserved3; + u8 addr2; + u8 addr1; + u16 vlan; + u8 addr6; + u8 addr5; + u8 addr4; + u8 addr3; +} __attribute__((packed)); + +struct vlan_table_entry +{ + u8 reserved1; + u8 reserved2; + u8 reserved3; + u8 reserved4; + u8 reserved5; + u8 reserved6; + u16 vlan; + u8 member; + u8 mc_unreg; + u8 mc_reg; + u8 untag; +} __attribute__((packed)); + +union ale_entry { + struct address_table_entry addr; + struct vlan_table_entry vlan; + u32 val[3]; + u8 byte[12]; +}; + +enum entry_type { + FREE_ENTRY = 0, + ADDR_ENTRY, + VLAN_ENTRY, + VLAN_ADDR_ENTRY, + LAST_ENTRY +}; + +static char *fwd_state_name[] = { + "Forwarding", + "Blocking/Forwarding/Learning", + "Forwarding/Learning", + "Forwarding", +}; + +static char *type_name[] = { + "free entry", + "address entry", + "VLAN entry", + "VLAN address entry", + "invalid" +}; + +enum entry_type decode_type(union ale_entry *entry) +{ + /* Entry Type (61:60) */ + return (entry->byte[7] >> 4) & 0x3; +} + +static void print_addr(u8 *data) +{ + printf("%02x:%02x:%02x:%02x:%02x:%02x", + data[5], data[4], data[11], data[10], data[9], data[8]); +} + +static void decode_multi_addr(union ale_entry *entry, int vlan) +{ + printf(" MULTI: "); + print_addr(entry->byte); + printf(" %s", fwd_state_name[entry->addr.vlan >> 14]); + printf("%s", (entry->addr.port & 0x02) ? " Super" : ""); + printf(" Ports: 0x%x", (entry->addr.port >> 2) & 0x3); + if (vlan) + printf(" VLAN: %04d", entry->addr.vlan & 0x0fff); + printf("\n"); +} + +static void decode_uni_addr(union ale_entry *entry, int vlan) +{ + printf(" UNI : "); + print_addr(entry->byte); + printf("%s", (entry->addr.port & 0x01) ? " Secure" : ""); + printf("%s", (entry->addr.port & 0x02) ? " Block" : ""); + printf("%s", (entry->addr.port & 0x20) ? " DLR" : ""); + printf(" Ports: 0x%x", (entry->addr.port >> 2) & 0x3); + if (vlan) + printf(" VLAN: %04d", entry->addr.vlan & 0x0fff); + printf("\n"); +} + +static void decode_oui_addr(union ale_entry *entry) +{ + printf(" OUI : "); + print_addr(entry->byte); + printf("\n"); +} + +static void decode_vlan(union ale_entry *entry) +{ + printf(" VLAN "); + printf("%04d: ", entry->vlan.vlan & 0x0fff); + printf("member: 0x%x ", entry->vlan.member & 0x7); + printf("mc flood unreg: 0x%x ", entry->vlan.mc_unreg & 0x7); + printf("mc flood reg: 0x%x ", entry->vlan.mc_reg & 0x7); + printf("untag: 0x%x\n", entry->vlan.untag & 0x7); +} + +static enum entry_type decode_ale_entry(unsigned int idx, const u8 *data, + bool last_was_free) +{ + union ale_entry *entry = (union ale_entry *) data; + enum entry_type type; + + entry = entry + idx; + type = decode_type(entry); + + if (!last_was_free || type != FREE_ENTRY) + printf("%04d: %s\n", idx, type_name[type]); + + switch (type) + { + case FREE_ENTRY: + goto out; + break; + + case ADDR_ENTRY: + /* Multicast: OUI 01:00:5e:xx:xx:xx */ + if (entry->addr.addr1 == 0x01) + decode_multi_addr(entry, 0); + else + if ((entry->addr.vlan >> 14) == 0x2) + decode_oui_addr(entry); + else + decode_uni_addr(entry, 0); + break; + + case VLAN_ENTRY: + decode_vlan(entry); + break; + + case VLAN_ADDR_ENTRY: + /* Check for Individual/Group bit */ + if (entry->addr.addr1 & 0x01) + decode_multi_addr(entry, 1); + else + decode_uni_addr(entry, 1); + break; + + default: + printf("internal failure.\n"); + } + +out: + return type; +} + +int cpsw_dump_regs(struct ethtool_drvinfo *info __maybe_unused, + struct ethtool_regs *regs) +{ + unsigned int entries = regs->len/ALE_ENTRY_BYTES; + enum entry_type type = LAST_ENTRY; + unsigned int i; + + printf("ALE Entries (%d):\n", entries); + + for (i = 0; i < entries; i++) + type = decode_ale_entry(i, regs->data, (type == FREE_ENTRY)); + + return 0; +} @@ -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; @@ -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 */ @@ -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; @@ -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; @@ -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 062695a..c0c37a4 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 @@ -75,7 +79,7 @@ .\" \(*NC - Network Classifier type values .\" .ds NC \fBether\fP|\fBip4\fP|\fBtcp4\fP|\fBudp4\fP|\fBsctp4\fP|\fBah4\fP|\fBesp4\fP|\fBip6\fP|\fBtcp6\fP|\fBudp6\fP|\fBah6\fP|\fBesp6\fP|\fBsctp6\fP -.. +. .\" .\" Start URL. .de UR @@ -113,7 +117,7 @@ . hy \\n(HY .. . -.TH ETHTOOL 8 "September 2019" "Ethtool version @VERSION@" +.TH ETHTOOL 8 "September 2023" "Ethtool version @VERSION@" .SH NAME ethtool \- query or control network driver and hardware settings . @@ -129,6 +133,23 @@ ethtool \- query or control network driver and hardware settings .HP .B ethtool \-\-version .HP +.B ethtool +.BN --debug +.I args +.HP +.B ethtool [--json] +.I args +.HP +.B ethtool [-I | --include-statistics] +.I args +.HP +.B ethtool \-\-monitor +[ +.I command +] [ +.I devname +] +.HP .B ethtool \-a|\-\-show\-pause .I devname .HP @@ -165,6 +186,11 @@ ethtool \- query or control network driver and hardware settings .BN tx\-usecs\-high .BN tx\-frames\-high .BN sample\-interval +.B2 cqe\-mode\-rx on off +.B2 cqe\-mode\-tx on off +.BN tx\-aggr\-max\-bytes +.BN tx\-aggr\-max\-frames +.BN tx\-aggr\-time\-usecs .HP .B ethtool \-g|\-\-show\-ring .I devname @@ -175,6 +201,11 @@ ethtool \- query or control network driver and hardware settings .BN rx\-mini .BN rx\-jumbo .BN tx +.BN rx\-buf\-len +.BN cqe\-size +.BN tx\-push +.BN rx\-push +.BN tx\-push\-buf\-len .HP .B ethtool \-i|\-\-driver .I devname @@ -219,6 +250,12 @@ ethtool \- query or control network driver and hardware settings .HP .B ethtool \-S|\-\-statistics .I devname +.RB [\fB\-\-all\-groups\fP|\fB\-\-groups +.RB [\fBeth\-phy\fP] +.RB [\fBeth\-mac\fP] +.RB [\fBeth\-ctrl\fP] +.RB [\fBrmon\fP] +.RB ] .HP .B ethtool \-\-phy\-statistics .I devname @@ -230,17 +267,24 @@ ethtool \- query or control network driver and hardware settings .B ethtool \-s .I devname .BN speed +.BN lanes .B2 duplex half full -.B4 port tp aui bnc mii fibre +.B4 port tp aui bnc mii fibre da .B3 mdix auto on off .B2 autoneg on off -.BN advertise +.RB [ advertise \ \fIN\fP[\fB/\fP\fIM\fP] +| +.BI advertise \ mode +.A1 on off +.RB ...] .BN phyad .B2 xcvr internal external -.RB [ wol \ \*(WO] +.RB [ wol \ \fIN\fP[\fB/\fP\fIM\fP] +.RB | \ wol \ \*(WO] .RB [ sopass \ \*(MA] +.RB [ master-slave \ \*(MS] .RB [ msglvl -.IR N \ | +.IR N\fP[/\fIM\fP] \ | .BI msglvl \ type .A1 on off .RB ...] @@ -331,6 +375,9 @@ ethtool \- query or control network driver and hardware settings .B2 hex on off .BN offset .BN length +.BN page +.BN bank +.BN i2c .HP .B ethtool \-\-show\-priv\-flags .I devname @@ -374,6 +421,20 @@ 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 [ tx-buf-size ] +.RB [ pfc-prevention-tout ] +.HP +.B ethtool \-\-set\-tunable +.I devname +.BN rx\-copybreak +.BN tx\-copybreak +.BN tx\-buf\-size +.BN pfc\-prevention\-tout +.HP .B ethtool \-\-reset .I devname .BN flags @@ -404,7 +465,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 @@ -412,7 +473,67 @@ ethtool \- query or control network driver and hardware settings .IR %x ] .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 +.HP +.B ethtool \-\-show\-module +.I devname +.HP +.B ethtool \-\-set\-module +.I devname +.RB [ power\-mode\-policy +.BR high | auto ] +.HP +.B ethtool \-\-get\-plca\-cfg +.I devname +.HP +.B ethtool \-\-set\-plca\-cfg +.I devname +.RB [ enable +.BR on | off ] +.BN node\-id N +.BN node\-cnt N +.BN to\-tmr N +.BN burst\-cnt N +.BN burst\-tmr N +.HP +.B ethtool \-\-get\-plca\-status +.I devname +.HP +.B ethtool \-\-show\-mm +.I devname +.HP +.B ethtool \-\-set\-mm +.I devname +.RB [ verify\-enabled +.BR on | off ] +.RB [ verify\-time +.BR N ] +.RB [ tx\-enabled +.BR on | off ] +.RB [ pmac\-enabled +.BR on | off ] +.RB [ tx\-min\-frag\-size +.BR N ] +.HP +.B ethtool \-\-show\-pse +.I devname +.HP +.B ethtool \-\-set\-pse +.I devname +.RB [ podl\-pse\-admin\-control +.BR enable | disable ] . .\" Adjust lines (i.e. full justification) and hyphenate. .ad @@ -437,8 +558,34 @@ Shows a short help message. .B \-\-version Shows the ethtool version number. .TP +.BI \-\-debug \ N +Turns on debugging messages. Argument is interpreted as a mask: +.TS +nokeep; +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. +.RS 4 +.TP +.A3 \fB\-\-src \fBaggregate\fP \fBemac\fP \fBpmac\fP +If the MAC Merge layer is supported, request a particular source of device +statistics (eMAC or pMAC, or their aggregate). Only valid if ethtool was +invoked with the +.B \-I \-\-include\-statistics +argument. +.RE .TP .B \-A \-\-pause Changes the pause parameters of the specified Ethernet device. @@ -478,6 +625,22 @@ Changes the number of ring entries for the Rx Jumbo ring. .TP .BI tx \ N Changes the number of ring entries for the Tx ring. +.TP +.BI rx\-buf\-len \ N +Changes the size of a buffer in the Rx ring. +.TP +.BI cqe\-size \ N +Changes the size of completion queue event. +.TP +.BI tx\-push \ on|off +Specifies whether TX push should be enabled. +.TP +.BI rx\-push \ on|off +Specifies whether RX push should be enabled. +.TP +.BI tx\-push\-buf\-len \ N +Specifies the maximum number of bytes of a transmitted packet a driver can push +directly to the underlying device .RE .TP .B \-i \-\-driver @@ -582,8 +745,24 @@ Restarts auto-negotiation on the specified Ethernet device, if auto-negotiation is enabled. .TP .B \-S \-\-statistics -Queries the specified network device for NIC- and driver-specific -statistics. +Queries the specified network device for standard (IEEE, IETF, etc.), or NIC- +and driver-specific statistics. NIC- and driver-specific statistics are +requested when no group of statistics is specified. + +NIC- and driver-specific statistics and standard statistics are independent, +devices may implement either, both or none. There is little commonality between +naming of NIC- and driver-specific statistics across vendors. +.RS 4 +.TP +.B \fB\-\-all\-groups +.TP +.B \fB\-\-groups [\fBeth\-phy\fP] [\fBeth\-mac\fP] [\fBeth\-ctrl\fP] [\fBrmon\fP] +Request groups of standard device statistics. +.TP +.A3 \fB\-\-src \fBaggregate\fP \fBemac\fP \fBpmac\fP +If the MAC Merge layer is supported, request a particular source of device +statistics (eMAC or pMAC, or their aggregate). +.RE .TP .B \-\-phy\-statistics Queries the specified network device for PHY specific statistics. @@ -616,12 +795,29 @@ Set speed in Mb/s. .B ethtool with just the device name as an argument will show you the supported device speeds. .TP +.BI lanes \ N +Set number of lanes. +.TP .A2 duplex half full Sets full or half duplex mode. .TP -.A4 port tp aui bnc mii fibre +.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 @@ -643,26 +839,32 @@ nokeep; lB l lB. 0x001 10baseT Half 0x002 10baseT Full +0x100000000000000000000000 10baseT1L Full +0x8000000000000000000000000 10baseT1S Full +0x10000000000000000000000000 10baseT1S Half +0x20000000000000000000000000 10baseT1S_P2MP Half 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 @@ -693,11 +895,37 @@ 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 +0x200000000000000000000000 800000baseCR8 Full +0x400000000000000000000000 800000baseKR8 Full +0x800000000000000000000000 800000baseDR8 Full +0x1000000000000000000000000 800000baseDR8_2 Full +0x2000000000000000000000000 800000baseSR8 Full +0x4000000000000000000000000 800000baseVR8 Full .TE .TP .BI phyad \ N @@ -1045,6 +1273,17 @@ Changes the number of multi-purpose channels. Retrieves and if possible decodes the EEPROM from plugin modules, e.g SFP+, QSFP. If the driver and module support it, the optical diagnostic information is also read and decoded. +When either one of +.I page, +.I bank +or +.I i2c +parameters is specified, dumps only of a single page or its portion is +allowed. In such a case +.I offset +and +.I length +parameters are treated relatively to EEPROM page boundaries. .TP .B \-\-show\-priv\-flags Queries the specified network device for its private flags. The @@ -1152,6 +1391,40 @@ 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 tx\-buf\-size +Get the current tx copybreak buffer size 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 tx\-buf\-size \ N +Set the tx copybreak buffer size 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 @@ -1204,7 +1477,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. @@ -1217,6 +1490,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 @@ -1232,6 +1506,250 @@ If queue_mask is not set, the sub command will be applied to all queues. Sub command to apply. The supported sub commands include --show-coalesce and --coalesce. .RE +.TP +.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 +.TP +.I command +If argument matching a command is used, ethtool only shows notifications of +this type. Without such argument or with --all, all notification types are +shown. +.TP +.I devname +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 +.TP +.B \-\-show\-module +Show the transceiver module's parameters. +.RE +.TP +.B \-\-set\-module +Set the transceiver module's parameters. +.RS 4 +.TP +.A2 power-mode-policy high auto +Set the power mode policy for the module. When set to \fBhigh\fR, the module +always operates at high power mode. When set to \fBauto\fR, the module is +transitioned by the host to high power mode when the first port using it is put +administratively up and to low power mode when the last port using it is put +administratively down. The power mode policy can be set before a module is +plugged-in. +.RE +.TP +.B \-\-get\-plca\-cfg +Show the current PLCA parameters for the given interface. +.RE +.TP +.B \-\-set\-plca\-cfg +Change the PLCA settings for the given interface. +.RS 4 +.TP +.A2 enable on off +Enables or disables the PLCA function. When the PLCA RS is disabled (default), +the PHY operates in plain CSMA/CD mode. To enable PLCA, the PHY must be assigned +a unique \fBplca\-id\fR other than 255. This one can be configured concurrently +with the enable parameter. The \fBenable\fR parameter maps to IEEE 802.3cg-2019 +clause 30.16.1.1.1 (aPLCAAdminState) and clause 30.16.1.2.1 (acPLCAAdminControl). +.TP +.BI node\-id \ N +The unique node identifier in the range [0 .. 255]. Node ID 0 is reserved for +the coordinator node, the one generating the BEACON signal. There must be +exactly one coordinator on a PLCA network. Setting the node ID to 255 (default) +disables the node. This parameter maps to IEEE 802.3cg-2019 clause 30.16.1.1.4 +(aPLCALocalNodeID). +.TP +.BI node\-cnt \ N +The node-cnt [1 .. 255] should be set after the maximum number of nodes that +can be plugged to the multi-drop network. This parameter regulates the minimum +length of the PLCA cycle. Therefore, it is only meaningful for the coordinator +node (\fBnod-id\fR = 0). Setting this parameter on a follower node has no +effect. The \fBnode\-cnt\fR parameter maps to IEEE 802.3cg-2019 clause +30.16.1.1.3 (aPLCANodeCount). +.TP +.BI to\-tmr \ N +The TO timer parameter sets the value of the transmit opportunity timer in +bit-times, and shall be set equal across all the nodes sharing the same +medium for PLCA to work. The default value of 32 is enough to cover a link of +roughly 50 mt. This parameter maps to IEEE 802.3cg-2019 clause 30.16.1.1.5 +(aPLCATransmitOpportunityTimer). +.TP +.BI burst\-cnt \ N +The \fBburst\-cnt\fR parameter [0 .. 255] indicates the extra number of packets +that the node is allowed to send during a single transmit opportunity. +By default, this attribute is 0, meaning that the node can send a sigle frame +per TO. When greater than 0, the PLCA RS keeps the TO after any transmission, +waiting for the MAC to send a new frame for up to \fBburst\-tmr\fR BTs. This can +only happen a number of times per PLCA cycle up to the value of this parameter. +After that, the burst is over and the normal counting of TOs resumes. +This parameter maps to IEEE 802.3cg-2019 clause 30.16.1.1.6 (aPLCAMaxBurstCount). +.TP +.BI burst\-tmr \ N +The \fBburst\-tmr\fR parameter [0 .. 255] sets how many bit-times the PLCA RS +waits for the MAC to initiate a new transmission when \fBburst\-cnt\fR is +greater than 0. If the MAC fails to send a new frame within this time, the burst +ends and the counting of TOs resumes. Otherwise, the new frame is sent as part +of the current burst. This parameter maps to IEEE 802.3cg-2019 clause +30.16.1.1.7 (aPLCABurstTimer). The value of \fBburst\-tmr\fR should be set +greater than the Inter-Frame-Gap (IFG) time of the MAC (plus some margin) +for PLCA burst mode to work as intended. +.RE +.TP +.B \-\-get\-plca\-status +Show the current PLCA status for the given interface. If \fBon\fR, the PHY is +successfully receiving or generating the BEACON signal. If \fBoff\fR, the PLCA +function is temporarily disabled and the PHY is operating in plain CSMA/CD mode. +.RE +.TP +.B \-\-show\-mm +Show the MAC Merge layer state. The ethtool argument +.B \-I \-\-include\-statistics +can be used with this command, and MAC Merge layer statistics counters will +also be retrieved. +.RS 4 +.TP +.B pmac-enabled +Shows whether the pMAC is enabled and capable of receiving traffic and SMD-V +frames (and responding to them with SMD-R replies). +.TP +.B tx-enabled +Shows whether transmission on the pMAC is administratively enabled. +.TP +.B tx-active +Shows whether transmission on the pMAC is active (verification is either +successful, or was disabled). +.TP +.B tx-min-frag-size +Shows the minimum size (in octets) of transmitted non-final fragments which +can be received by the link partner. Corresponds to the standard addFragSize +variable using the formula: + +tx-min-frag-size = 64 * (1 + addFragSize) - 4 +.TP +.B rx-min-frag-size +Shows the minimum size (in octets) of non-final fragments which the local +device supports receiving. +.TP +.B verify-enabled +Shows whether the verification state machine is enabled. This process, if +successful, ensures that preemptible frames transmitted by the local device +will not be dropped as error frames by the link partner. +.TP +.B verify-time +Shows the interval in ms between verification attempts, represented as an +integer between 1 and 128 ms. The standard defines a fixed number of +verification attempts (verifyLimit) before failing the verification process. +.TP +.B max-verify-time +Shows the maximum value for verify-time accepted by the local device, which +may be less than 128 ms. +.TP +.B verify-status +Shows the current state of the verification state machine of the local device. +Values can be +.B INITIAL, +.B VERIFYING, +.B SUCCEEDED, +.B FAILED +or +.B DISABLED. + +.RE +.TP +.B \-\-set\-mm +Set the MAC Merge layer parameters. +.RS 4 +.TP +.A2 pmac-enabled \ on off +Enable reception for the pMAC. +.TP +.A2 tx-enabled \ on off +Administatively enable transmission for the pMAC. +.TP +.B tx-min-frag-size \ N +Set the minimum size (in octets) of transmitted non-final fragments which can +be received by the link partner. +.TP +.A2 verify-enabled \ on off +Enable or disable the verification state machine. +.TP +.B verify-time \ N +Set the interval in ms between verification attempts. + +.RE +.TP +.B \-\-show\-pse +Show the current Power Sourcing Equipment (PSE) status for the given interface. +.RS 4 +.TP +.B podl-pse-admin-state +This attribute indicates the operational status of PoDL PSE functions, which +can be modified using the +.B podl-pse-admin-control +parameter. It corresponds to IEEE 802.3-2018 30.15.1.1.2 (aPoDLPSEAdminState), +with potential values being +.B enabled, disabled +.TP +.B podl-pse-power-detection-status +This attribute indicates the power detection status of the PoDL PSE. The +status depend on internal PSE state machine and automatic PD classification +support. It corresponds to IEEE 802.3-2018 30.15.1.1.3 +(aPoDLPSEPowerDetectionStatus) with potential values being +.B disabled, searching, delivering power, sleep, idle, error +.RE + +.RE +.TP +.B \-\-set\-pse +Set Power Sourcing Equipment (PSE) parameters. +.RS 4 +.TP +.A2 podl-pse-admin-control \ enable disable +This parameter manages PoDL PSE Admin operations in accordance with the IEEE +802.3-2018 30.15.1.2.1 (acPoDLPSEAdminControl) specification. + .SH BUGS Not supported (in part or whole) on all network drivers. .SH AUTHOR @@ -31,6 +31,7 @@ #include "internal.h" #include <string.h> +#include <strings.h> #include <stdlib.h> #include <sys/stat.h> #include <stdio.h> @@ -40,42 +41,23 @@ #include <sys/utsname.h> #include <limits.h> #include <ctype.h> +#include <inttypes.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <linux/ioctl.h> #include <linux/sockios.h> #include <linux/netlink.h> +#include "common.h" +#include "netlink/extapi.h" + #ifndef MAX_ADDR_LEN #define MAX_ADDR_LEN 32 #endif -#ifndef HAVE_NETIF_MSG -enum { - NETIF_MSG_DRV = 0x0001, - NETIF_MSG_PROBE = 0x0002, - NETIF_MSG_LINK = 0x0004, - NETIF_MSG_TIMER = 0x0008, - NETIF_MSG_IFDOWN = 0x0010, - NETIF_MSG_IFUP = 0x0020, - NETIF_MSG_RX_ERR = 0x0040, - NETIF_MSG_TX_ERR = 0x0080, - NETIF_MSG_TX_QUEUED = 0x0100, - NETIF_MSG_INTR = 0x0200, - NETIF_MSG_TX_DONE = 0x0400, - NETIF_MSG_RX_STATUS = 0x0800, - NETIF_MSG_PKTDATA = 0x1000, - NETIF_MSG_HW = 0x2000, - NETIF_MSG_WOL = 0x4000, -}; -#endif - -#ifndef NETLINK_GENERIC -#define NETLINK_GENERIC 16 -#endif - #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) static void exit_bad_args(void) __attribute__((noreturn)); @@ -88,6 +70,16 @@ static void exit_bad_args(void) exit(1); } +static void exit_nlonly_param(const char *name) __attribute__((noreturn)); + +static void exit_nlonly_param(const char *name) +{ + fprintf(stderr, + "ethtool: parameter '%s' can be used only with netlink\n", + name); + exit(1); +} + typedef enum { CMDL_NONE, CMDL_BOOL, @@ -119,74 +111,6 @@ struct cmdline_info { void *seen_val; }; -struct flag_info { - const char *name; - u32 value; -}; - -static const struct flag_info flags_msglvl[] = { - { "drv", NETIF_MSG_DRV }, - { "probe", NETIF_MSG_PROBE }, - { "link", NETIF_MSG_LINK }, - { "timer", NETIF_MSG_TIMER }, - { "ifdown", NETIF_MSG_IFDOWN }, - { "ifup", NETIF_MSG_IFUP }, - { "rx_err", NETIF_MSG_RX_ERR }, - { "tx_err", NETIF_MSG_TX_ERR }, - { "tx_queued", NETIF_MSG_TX_QUEUED }, - { "intr", NETIF_MSG_INTR }, - { "tx_done", NETIF_MSG_TX_DONE }, - { "rx_status", NETIF_MSG_RX_STATUS }, - { "pktdata", NETIF_MSG_PKTDATA }, - { "hw", NETIF_MSG_HW }, - { "wol", NETIF_MSG_WOL }, -}; - -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 */ @@ -195,7 +119,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]; }; @@ -307,9 +231,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++) { @@ -375,7 +299,7 @@ static void parse_generic_cmdline(struct cmd_context *ctx, case CMDL_IP4: { u32 *p = info[idx].wanted_val; struct in_addr in; - if (!inet_aton(argp[i], &in)) + if (!inet_pton(AF_INET, argp[i], &in)) exit_bad_args(); *p = in.s_addr; break; @@ -424,26 +348,6 @@ static void flag_to_cmdline_info(const char *name, u32 value, cli->seen_val = mask; } -static void -print_flags(const struct flag_info *info, unsigned int n_info, u32 value) -{ - const char *sep = ""; - - while (n_info) { - if (value & info->value) { - printf("%s%s", sep, info->name); - sep = " "; - value &= ~info->value; - } - ++info; - --n_info; - } - - /* Print any unrecognised flags in hex */ - if (value) - printf("%s%#x", sep, value); -} - static int rxflow_str_to_type(const char *str) { int flow_type = 0; @@ -470,7 +374,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 @@ -496,9 +400,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, @@ -547,6 +451,38 @@ 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, + ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT, + ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT, + ETHTOOL_LINK_MODE_10baseT1S_Full_BIT, + ETHTOOL_LINK_MODE_10baseT1S_Half_BIT, + ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, }; static const enum ethtool_link_mode_bit_indices additional_advertised_flags_bits[] = { @@ -562,6 +498,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; @@ -636,20 +573,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, @@ -742,10 +675,79 @@ 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" }, + { 0, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + "10baseT1L/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT, + "800000baseCR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT, + "800000baseKR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT, + "800000baseDR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT, + "800000baseDR8_2/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT, + "800000baseSR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT, + "800000baseVR8/Full" }, + { 0, ETHTOOL_LINK_MODE_10baseT1S_Full_BIT, + "10baseT1S/Full" }, + { 1, ETHTOOL_LINK_MODE_10baseT1S_Half_BIT, + "10baseT1S/Half" }, + { 0, ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, + "10baseT1S/Half" }, }; 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; @@ -814,6 +816,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"); @@ -902,31 +910,9 @@ dump_link_usettings(const struct ethtool_link_usettings *link_usettings) (link_usettings->base.autoneg == AUTONEG_DISABLE) ? "off" : "on"); - if (link_usettings->base.port == PORT_TP) { - fprintf(stdout, " MDI-X: "); - if (link_usettings->base.eth_tp_mdix_ctrl == ETH_TP_MDI) { - fprintf(stdout, "off (forced)\n"); - } else if (link_usettings->base.eth_tp_mdix_ctrl - == ETH_TP_MDI_X) { - fprintf(stdout, "on (forced)\n"); - } else { - switch (link_usettings->base.eth_tp_mdix) { - case ETH_TP_MDI: - fprintf(stdout, "off"); - break; - case ETH_TP_MDI_X: - fprintf(stdout, "on"); - break; - default: - fprintf(stdout, "Unknown"); - break; - } - if (link_usettings->base.eth_tp_mdix_ctrl - == ETH_TP_MDI_AUTO) - fprintf(stdout, " (auto)"); - fprintf(stdout, "\n"); - } - } + if (link_usettings->base.port == PORT_TP) + dump_mdix(link_usettings->base.eth_tp_mdix, + link_usettings->base.eth_tp_mdix_ctrl); return 0; } @@ -998,58 +984,6 @@ static int parse_wolopts(char *optstr, u32 *data) return 0; } -static char *unparse_wolopts(int wolopts) -{ - static char buf[16]; - char *p = buf; - - memset(buf, 0, sizeof(buf)); - - if (wolopts) { - if (wolopts & WAKE_PHY) - *p++ = 'p'; - if (wolopts & WAKE_UCAST) - *p++ = 'u'; - if (wolopts & WAKE_MCAST) - *p++ = 'm'; - if (wolopts & WAKE_BCAST) - *p++ = 'b'; - if (wolopts & WAKE_ARP) - *p++ = 'a'; - if (wolopts & WAKE_MAGIC) - *p++ = 'g'; - if (wolopts & WAKE_MAGICSECURE) - *p++ = 's'; - if (wolopts & WAKE_FILTER) - *p++ = 'f'; - } else { - *p = 'd'; - } - - return buf; -} - -static int dump_wol(struct ethtool_wolinfo *wol) -{ - fprintf(stdout, " Supports Wake-on: %s\n", - unparse_wolopts(wol->supported)); - fprintf(stdout, " Wake-on: %s\n", - unparse_wolopts(wol->wolopts)); - if (wol->supported & WAKE_MAGICSECURE) { - int i; - int delim = 0; - - fprintf(stdout, " SecureOn password: "); - for (i = 0; i < SOPASS_MAX; i++) { - fprintf(stdout, "%s%02x", delim?":":"", wol->sopass[i]); - delim = 1; - } - fprintf(stdout, "\n"); - } - - return 0; -} - static int parse_rxfhashopts(char *optstr, u32 *data) { *data = 0; @@ -1183,12 +1117,12 @@ static int parse_hkey(char **rss_hkey, u32 key_size, return 0; } +#ifdef ETHTOOL_ENABLE_PRETTY_DUMP static const struct { const char *name; int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs); } driver_list[] = { -#ifdef ETHTOOL_ENABLE_PRETTY_DUMP { "8139cp", realtek_dump_regs }, { "8139too", realtek_dump_regs }, { "r8169", realtek_dump_regs }, @@ -1221,8 +1155,15 @@ static const struct { { "lan78xx", lan78xx_dump_regs }, { "dsa", dsa_dump_regs }, { "fec", fec_dump_regs }, -#endif + { "igc", igc_dump_regs }, + { "bnxt_en", bnxt_dump_regs }, + { "cpsw-switch", cpsw_dump_regs }, + { "lan743x", lan743x_dump_regs }, + { "fsl_enetc", fsl_enetc_dump_regs }, + { "fsl_enetc_vf", fsl_enetc_dump_regs }, + { "hns3", hns3_dump_regs }, }; +#endif void dump_hex(FILE *file, const u8 *data, int len, int offset) { @@ -1241,14 +1182,15 @@ 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; - if (gregs_dump_raw) { fwrite(regs->data, regs->len, 1, stdout); goto nested; } - if (!gregs_dump_hex) +#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + if (!gregs_dump_hex) { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(driver_list); i++) if (!strncmp(driver_list[i].name, info->driver, ETHTOOL_BUSINFO_LEN)) { @@ -1260,6 +1202,8 @@ static int dump_regs(int gregs_dump_raw, int gregs_dump_hex, */ break; } + } +#endif dump_hex(stdout, regs->data, regs->len, 0); @@ -1276,7 +1220,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) { @@ -1298,7 +1242,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"); @@ -1529,7 +1474,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; @@ -1545,12 +1490,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 */ @@ -1581,7 +1526,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 */ @@ -1696,6 +1641,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 @@ -1800,7 +1747,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; @@ -1834,8 +1783,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) { @@ -1867,7 +1816,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 = @@ -1989,10 +1938,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; @@ -2032,12 +1995,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; @@ -2101,12 +2082,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; @@ -2159,7 +2158,7 @@ static int do_gchannels(struct cmd_context *ctx) if (err) return err; } else { - perror("Cannot get device channel parameters\n"); + perror("Cannot get device channel parameters"); return 1; } return 0; @@ -2216,50 +2215,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) @@ -2315,7 +2402,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; @@ -2398,12 +2485,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) { @@ -2424,33 +2512,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) { @@ -2470,14 +2562,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) || @@ -2511,7 +2603,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) { @@ -2578,9 +2670,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; } @@ -2631,8 +2725,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; @@ -2837,8 +2931,7 @@ static int do_gset(struct cmd_context *ctx) fprintf(stdout, " Current message level: 0x%08x (%d)\n" " ", edata.data, edata.data); - print_flags(flags_msglvl, ARRAY_SIZE(flags_msglvl), - edata.data); + print_flags(flags_msglvl, n_flags_msglvl, edata.data); fprintf(stdout, "\n"); allfail = 0; } else if (errno != EOPNOTSUPP) { @@ -2884,13 +2977,13 @@ static int do_sset(struct cmd_context *ctx) int msglvl_changed = 0; u32 msglvl_wanted = 0; u32 msglvl_mask = 0; - struct cmdline_info cmdline_msglvl[ARRAY_SIZE(flags_msglvl)]; - int argc = ctx->argc; + struct cmdline_info cmdline_msglvl[n_flags_msglvl]; + unsigned int argc = ctx->argc; char **argp = ctx->argp; - int i; + unsigned int i; int err = 0; - for (i = 0; i < ARRAY_SIZE(flags_msglvl); i++) + for (i = 0; i < n_flags_msglvl; i++) flag_to_cmdline_info(flags_msglvl[i].name, flags_msglvl[i].value, &msglvl_wanted, &msglvl_mask, @@ -3019,6 +3112,8 @@ static int do_sset(struct cmd_context *ctx) ARRAY_SIZE(cmdline_msglvl)); break; } + } else if (!strcmp(argp[i], "master-slave")) { + exit_nlonly_param(argp[i]); } else { exit_bad_args(); } @@ -3071,6 +3166,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; @@ -3247,9 +3345,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; @@ -3343,11 +3453,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; @@ -3363,7 +3487,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) @@ -3393,16 +3517,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; @@ -3418,12 +3560,16 @@ static int do_seeprom(struct cmd_context *ctx) return 74; } - if (seeprom_value_seen) + if (seeprom_value_seen && !seeprom_length_seen) seeprom_length = 1; - - if (seeprom_length == -1) + else if (!seeprom_length_seen) seeprom_length = drvinfo.eedump_len; + if (seeprom_value_seen && (seeprom_length != 1)) { + fprintf(stderr, "value requires length 1\n"); + return 1; + } + if (drvinfo.eedump_len < seeprom_offset + seeprom_length) { fprintf(stderr, "offset & length out of bounds\n"); return 1; @@ -3760,27 +3906,6 @@ static int do_grxclass(struct cmd_context *ctx) return err ? 1 : 0; } -static void print_indir_table(struct cmd_context *ctx, - struct ethtool_rxnfc *ring_count, - u32 indir_size, u32 *indir) -{ - u32 i; - - printf("RX flow hash indirection table for %s with %llu RX ring(s):\n", - ctx->devname, ring_count->data); - - if (!indir_size) - printf("Operation not supported\n"); - - for (i = 0; i < indir_size; i++) { - if (i % 8 == 0) - printf("%5u: ", i); - printf(" %5u", indir[i]); - if (i % 8 == 7 || i == indir_size - 1) - fputc('\n', stdout); - } -} - static int do_grxfhindir(struct cmd_context *ctx, struct ethtool_rxnfc *ring_count) { @@ -3812,7 +3937,8 @@ static int do_grxfhindir(struct cmd_context *ctx, return 1; } - print_indir_table(ctx, ring_count, indir->size, indir->ring_index); + print_indir_table(ctx, ring_count->data, indir->size, + indir->ring_index); free(indir); return 0; @@ -3826,8 +3952,8 @@ static int do_grxfh(struct cmd_context *ctx) struct ethtool_rxfh *rss; u32 rss_context = 0; u32 i, indir_bytes; - int arg_num = 0; - char *hkey; + unsigned int arg_num = 0; + u8 *hkey; int err; while (arg_num < ctx->argc) { @@ -3877,21 +4003,13 @@ static int do_grxfh(struct cmd_context *ctx) return 1; } - print_indir_table(ctx, &ring_count, rss->indir_size, rss->rss_config); + print_indir_table(ctx, ring_count.data, rss->indir_size, + rss->rss_config); indir_bytes = rss->indir_size * sizeof(rss->rss_config[0]); - hkey = ((char *)rss->rss_config + indir_bytes); + hkey = ((u8 *)rss->rss_config + indir_bytes); - printf("RSS hash key:\n"); - if (!rss->key_size) - printf("Operation not supported\n"); - - for (i = 0; i < rss->key_size; i++) { - if (i == (rss->key_size - 1)) - printf("%02x\n", (u8) hkey[i]); - else - printf("%02x:", (u8) hkey[i]); - } + print_rss_hkey(hkey, rss->key_size); printf("RSS hash function:\n"); if (!rss->hfunc) { @@ -4024,7 +4142,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]); @@ -4290,7 +4408,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); @@ -4506,7 +4625,7 @@ static int do_getfwdump(struct cmd_context *ctx) err = send_ioctl(ctx, &edata); if (err < 0) { - perror("Can not get dump level\n"); + perror("Can not get dump level"); return 1; } if (dump_flag != ETHTOOL_GET_DUMP_DATA) { @@ -4516,14 +4635,14 @@ static int do_getfwdump(struct cmd_context *ctx) } data = calloc(1, offsetof(struct ethtool_dump, data) + edata.len); if (!data) { - perror("Can not allocate enough memory\n"); + perror("Can not allocate enough memory"); return 1; } data->cmd = ETHTOOL_GET_DUMP_DATA; data->len = edata.len; err = send_ioctl(ctx, data); if (err < 0) { - perror("Can not get dump data\n"); + perror("Can not get dump data"); err = 1; goto free; } @@ -4547,7 +4666,7 @@ static int do_setfwdump(struct cmd_context *ctx) dump.flag = dump_flag; err = send_ioctl(ctx, &dump); if (err < 0) { - perror("Can not set dump level\n"); + perror("Can not set dump level"); return 1; } return 0; @@ -4697,17 +4816,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, @@ -4725,7 +4862,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) @@ -4742,7 +4879,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; } @@ -4767,16 +4909,16 @@ static int do_getmodule(struct cmd_context *ctx) switch (modinfo.type) { #ifdef ETHTOOL_ENABLE_PRETTY_DUMP case ETH_MODULE_SFF_8079: - sff8079_show_all(eeprom->data); + sff8079_show_all_ioctl(eeprom->data); break; case ETH_MODULE_SFF_8472: - sff8079_show_all(eeprom->data); + sff8079_show_all_ioctl(eeprom->data); sff8472_show_all(eeprom->data); break; case ETH_MODULE_SFF_8436: case ETH_MODULE_SFF_8636: - sff8636_show_all(eeprom->data, - modinfo.eeprom_len); + sff8636_show_all_ioctl(eeprom->data, + modinfo.eeprom_len); break; #endif default: @@ -4819,10 +4961,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) @@ -4850,9 +5012,193 @@ 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_TX_COPYBREAK_BUF_SIZE] = "tx-buf-size", + [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, + }, + { .t_id = ETHTOOL_TX_COPYBREAK_BUF_SIZE, + .t_type_id = ETHTOOL_TUNABLE_U32, + .size = sizeof(u32), + .type = CMDL_U32, + }, +}; +#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]); + free(tuna); + 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); + free(tuna); + 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) @@ -4956,9 +5302,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(); @@ -5209,7 +5555,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; } @@ -5229,7 +5576,7 @@ static int do_gfec(struct cmd_context *ctx) } fprintf(stdout, "FEC parameters for %s:\n", ctx->devname); - fprintf(stdout, "Configured FEC encodings:"); + fprintf(stdout, "Supported/Configured FEC encodings:"); dump_fec(feccmd.fec); fprintf(stdout, "\n"); @@ -5245,7 +5592,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")) { @@ -5288,222 +5636,540 @@ int send_ioctl(struct cmd_context *ctx, void *cmd) static int show_usage(struct cmd_context *ctx); -static const struct option { - const char *opts; - int want_device; - int (*func)(struct cmd_context *); - char *help; - char *opthelp; -} args[] = { - { "-s|--change", 1, do_sset, "Change generic options", - " [ speed %d ]\n" - " [ duplex half|full ]\n" - " [ port tp|aui|bnc|mii|fibre ]\n" - " [ mdix auto|on|off ]\n" - " [ autoneg on|off ]\n" - " [ advertise %x ]\n" - " [ phyad %d ]\n" - " [ xcvr internal|external ]\n" - " [ wol p|u|m|b|a|g|s|f|d... ]\n" - " [ sopass %x:%x:%x:%x:%x:%x ]\n" - " [ msglvl %d | msglvl type on|off ... ]\n" }, - { "-a|--show-pause", 1, do_gpause, "Show pause options" }, - { "-A|--pause", 1, do_spause, "Set pause options", - " [ autoneg on|off ]\n" - " [ rx on|off ]\n" - " [ tx on|off ]\n" }, - { "-c|--show-coalesce", 1, do_gcoalesce, "Show coalesce options" }, - { "-C|--coalesce", 1, do_scoalesce, "Set coalesce options", - " [adaptive-rx on|off]\n" - " [adaptive-tx on|off]\n" - " [rx-usecs N]\n" - " [rx-frames N]\n" - " [rx-usecs-irq N]\n" - " [rx-frames-irq N]\n" - " [tx-usecs N]\n" - " [tx-frames N]\n" - " [tx-usecs-irq N]\n" - " [tx-frames-irq N]\n" - " [stats-block-usecs N]\n" - " [pkt-rate-low N]\n" - " [rx-usecs-low N]\n" - " [rx-frames-low N]\n" - " [tx-usecs-low N]\n" - " [tx-frames-low N]\n" - " [pkt-rate-high N]\n" - " [rx-usecs-high N]\n" - " [rx-frames-high N]\n" - " [tx-usecs-high N]\n" - " [tx-frames-high N]\n" - " [sample-interval N]\n" }, - { "-g|--show-ring", 1, do_gring, "Query RX/TX ring parameters" }, - { "-G|--set-ring", 1, do_sring, "Set RX/TX ring parameters", - " [ rx N ]\n" - " [ rx-mini N ]\n" - " [ rx-jumbo N ]\n" - " [ tx N ]\n" }, - { "-k|--show-features|--show-offload", 1, do_gfeatures, - "Get state of protocol offload and other features" }, - { "-K|--features|--offload", 1, do_sfeatures, - "Set protocol offload and other features", - " FEATURE on|off ...\n" }, - { "-i|--driver", 1, do_gdrv, "Show driver information" }, - { "-d|--register-dump", 1, do_gregs, "Do a register dump", - " [ raw on|off ]\n" - " [ file FILENAME ]\n" }, - { "-e|--eeprom-dump", 1, do_geeprom, "Do a EEPROM dump", - " [ raw on|off ]\n" - " [ offset N ]\n" - " [ length N ]\n" }, - { "-E|--change-eeprom", 1, do_seeprom, - "Change bytes in device EEPROM", - " [ magic N ]\n" - " [ offset N ]\n" - " [ length N ]\n" - " [ value N ]\n" }, - { "-r|--negotiate", 1, do_nway_rst, "Restart N-WAY negotiation" }, - { "-p|--identify", 1, do_phys_id, - "Show visible port identification (e.g. blinking)", - " [ TIME-IN-SECONDS ]\n" }, - { "-t|--test", 1, do_test, "Execute adapter self test", - " [ online | offline | external_lb ]\n" }, - { "-S|--statistics", 1, do_gnicstats, "Show adapter statistics" }, - { "--phy-statistics", 1, do_gphystats, - "Show phy statistics" }, - { "-n|-u|--show-nfc|--show-ntuple", 1, do_grxclass, - "Show Rx network flow classification options or rules", - " [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" - "tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n" - " rule %d ]\n" }, - { "-N|-U|--config-nfc|--config-ntuple", 1, do_srxclass, - "Configure Rx network flow classification options or rules", - " rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" - "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n" - " flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|" - "ip6|tcp6|udp6|ah6|esp6|sctp6\n" - " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" - " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" - " [ proto %d [m %x] ]\n" - " [ src-ip IP-ADDRESS [m IP-ADDRESS] ]\n" - " [ dst-ip IP-ADDRESS [m IP-ADDRESS] ]\n" - " [ tos %d [m %x] ]\n" - " [ tclass %d [m %x] ]\n" - " [ l4proto %d [m %x] ]\n" - " [ src-port %d [m %x] ]\n" - " [ dst-port %d [m %x] ]\n" - " [ spi %d [m %x] ]\n" - " [ vlan-etype %x [m %x] ]\n" - " [ vlan %x [m %x] ]\n" - " [ user-def %x [m %x] ]\n" - " [ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" - " [ action %d ] | [ vf %d queue %d ]\n" - " [ context %d ]\n" - " [ loc %d]] |\n" - " delete %d\n" }, - { "-T|--show-time-stamping", 1, do_tsinfo, - "Show time stamping capabilities" }, - { "-x|--show-rxfh-indir|--show-rxfh", 1, do_grxfh, - "Show Rx flow hash indirection table and/or RSS hash key", - " [ context %d ]\n" }, - { "-X|--set-rxfh-indir|--rxfh", 1, do_srxfh, - "Set Rx flow hash indirection table and/or RSS hash key", - " [ context %d|new ]\n" - " [ equal N | weight W0 W1 ... | default ]\n" - " [ hkey %x:%x:%x:%x:%x:.... ]\n" - " [ hfunc FUNC ]\n" - " [ delete ]\n" }, - { "-f|--flash", 1, do_flash, - "Flash firmware image from the specified file to a region on the device", - " FILENAME [ REGION-NUMBER-TO-FLASH ]\n" }, - { "-P|--show-permaddr", 1, do_permaddr, - "Show permanent hardware address" }, - { "-w|--get-dump", 1, do_getfwdump, - "Get dump flag, data", - " [ data FILENAME ]\n" }, - { "-W|--set-dump", 1, do_setfwdump, - "Set dump flag of the device", - " N\n"}, - { "-l|--show-channels", 1, do_gchannels, "Query Channels" }, - { "-L|--set-channels", 1, do_schannels, "Set Channels", - " [ rx N ]\n" - " [ tx N ]\n" - " [ other N ]\n" - " [ combined N ]\n" }, - { "--show-priv-flags", 1, do_gprivflags, "Query private flags" }, - { "--set-priv-flags", 1, do_sprivflags, "Set private flags", - " FLAG on|off ...\n" }, - { "-m|--dump-module-eeprom|--module-info", 1, do_getmodule, - "Query/Decode Module EEPROM information and optical diagnostics if available", - " [ raw on|off ]\n" - " [ hex on|off ]\n" - " [ offset N ]\n" - " [ length N ]\n" }, - { "--show-eee", 1, do_geee, "Show EEE settings"}, - { "--set-eee", 1, do_seee, "Set EEE settings", - " [ eee on|off ]\n" - " [ advertise %x ]\n" - " [ tx-lpi on|off ]\n" - " [ tx-timer %d ]\n"}, - { "--set-phy-tunable", 1, do_set_phy_tunable, "Set PHY tunable", - " [ downshift on|off [count N] ]\n" - " [ fast-link-down on|off [msecs N] ]\n" - " [ energy-detect-power-down on|off [msecs N] ]\n"}, - { "--get-phy-tunable", 1, do_get_phy_tunable, "Get PHY tunable", - " [ downshift ]\n" - " [ fast-link-down ]\n" - " [ energy-detect-power-down ]\n"}, - { "--reset", 1, do_reset, "Reset components", - " [ flags %x ]\n" - " [ mgmt ]\n" - " [ mgmt-shared ]\n" - " [ irq ]\n" - " [ irq-shared ]\n" - " [ dma ]\n" - " [ dma-shared ]\n" - " [ filter ]\n" - " [ filter-shared ]\n" - " [ offload ]\n" - " [ offload-shared ]\n" - " [ mac ]\n" - " [ mac-shared ]\n" - " [ phy ]\n" - " [ phy-shared ]\n" - " [ ram ]\n" - " [ ram-shared ]\n" - " [ ap ]\n" - " [ ap-shared ]\n" - " [ dedicated ]\n" - " [ all ]\n"}, - { "--show-fec", 1, do_gfec, "Show FEC settings"}, - { "--set-fec", 1, do_sfec, "Set FEC settings", - " [ encoding auto|off|rs|baser [...]]\n"}, - { "-Q|--per-queue", 1, do_perqueue, "Apply per-queue command." - "The supported sub commands include --show-coalesce, --coalesce", - " [queue_mask %x] SUB_COMMAND\n"}, - { "-h|--help", 0, show_usage, "Show this help" }, - { "--version", 0, do_version, "Show version number" }, +struct option { + const char *opts; + bool no_dev; + bool json; + int (*func)(struct cmd_context *); + nl_chk_t nlchk; + nl_func_t nlfunc; + const char *help; + const char *xhelp; +}; + +static const struct option args[] = { + { + /* "default" entry when no switch is used */ + .opts = "", + .func = do_gset, + .nlfunc = nl_gset, + .help = "Display standard information about device", + }, + { + .opts = "-s|--change", + .func = do_sset, + .nlfunc = nl_sset, + .help = "Change generic options", + .xhelp = " [ speed %d ]\n" + " [ lanes %d ]\n" + " [ duplex half|full ]\n" + " [ port tp|aui|bnc|mii|fibre|da ]\n" + " [ mdix auto|on|off ]\n" + " [ autoneg on|off ]\n" + " [ advertise %x[/%x] | mode on|off ... [--] ]\n" + " [ phyad %d ]\n" + " [ xcvr internal|external ]\n" + " [ 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 preferred-master|preferred-slave|forced-master|forced-slave ]\n" + }, + { + .opts = "-a|--show-pause", + .json = true, + .func = do_gpause, + .nlfunc = nl_gpause, + .help = "Show pause options", + .xhelp = " [ --src aggregate | emac | pmac ]\n" + }, + { + .opts = "-A|--pause", + .func = do_spause, + .nlfunc = nl_spause, + .help = "Set pause options", + .xhelp = " [ autoneg on|off ]\n" + " [ rx on|off ]\n" + " [ tx on|off ]\n" + }, + { + .opts = "-c|--show-coalesce", + .json = true, + .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" + " [rx-usecs N]\n" + " [rx-frames N]\n" + " [rx-usecs-irq N]\n" + " [rx-frames-irq N]\n" + " [tx-usecs N]\n" + " [tx-frames N]\n" + " [tx-usecs-irq N]\n" + " [tx-frames-irq N]\n" + " [stats-block-usecs N]\n" + " [pkt-rate-low N]\n" + " [rx-usecs-low N]\n" + " [rx-frames-low N]\n" + " [tx-usecs-low N]\n" + " [tx-frames-low N]\n" + " [pkt-rate-high N]\n" + " [rx-usecs-high N]\n" + " [rx-frames-high N]\n" + " [tx-usecs-high N]\n" + " [tx-frames-high N]\n" + " [sample-interval N]\n" + " [cqe-mode-rx on|off]\n" + " [cqe-mode-tx on|off]\n" + " [tx-aggr-max-bytes N]\n" + " [tx-aggr-max-frames N]\n" + " [tx-aggr-time-usecs N]\n" + }, + { + .opts = "-g|--show-ring", + .json = true, + .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" + " [ rx-jumbo N ]\n" + " [ tx N ]\n" + " [ rx-buf-len N ]\n" + " [ cqe-size N ]\n" + " [ tx-push on|off ]\n" + " [ rx-push on|off ]\n" + " [ tx-push-buf-len N]\n" + }, + { + .opts = "-k|--show-features|--show-offload", + .json = true, + .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" + }, + { + .opts = "-i|--driver", + .func = do_gdrv, + .help = "Show driver information" + }, + { + .opts = "-d|--register-dump", + .func = do_gregs, + .help = "Do a register dump", + .xhelp = " [ raw on|off ]\n" + " [ file FILENAME ]\n" + }, + { + .opts = "-e|--eeprom-dump", + .func = do_geeprom, + .help = "Do a EEPROM dump", + .xhelp = " [ raw on|off ]\n" + " [ offset N ]\n" + " [ length N ]\n" + }, + { + .opts = "-E|--change-eeprom", + .func = do_seeprom, + .help = "Change bytes in device EEPROM", + .xhelp = " [ magic N ]\n" + " [ offset N ]\n" + " [ length N ]\n" + " [ value N ]\n" + }, + { + .opts = "-r|--negotiate", + .func = do_nway_rst, + .help = "Restart N-WAY negotiation" + }, + { + .opts = "-p|--identify", + .func = do_phys_id, + .help = "Show visible port identification (e.g. blinking)", + .xhelp = " [ TIME-IN-SECONDS ]\n" + }, + { + .opts = "-t|--test", + .func = do_test, + .help = "Execute adapter self test", + .xhelp = " [ online | offline | external_lb ]\n" + }, + { + .opts = "-S|--statistics", + .json = true, + .func = do_gnicstats, + .nlchk = nl_gstats_chk, + .nlfunc = nl_gstats, + .help = "Show adapter statistics", + .xhelp = " [ --all-groups | --groups [eth-phy] [eth-mac] [eth-ctrl] [rmon] ]\n" + " [ --src aggregate | emac | pmac ]\n" + }, + { + .opts = "--phy-statistics", + .func = do_gphystats, + .help = "Show phy statistics" + }, + { + .opts = "-n|-u|--show-nfc|--show-ntuple", + .func = do_grxclass, + .help = "Show Rx network flow classification options or rules", + .xhelp = " [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" + "tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n" + " rule %d ]\n" + }, + { + .opts = "-N|-U|--config-nfc|--config-ntuple", + .func = do_srxclass, + .help = "Configure Rx network flow classification options or rules", + .xhelp = " rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" + "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n" + " flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|" + "ip6|tcp6|udp6|ah6|esp6|sctp6\n" + " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" + " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" + " [ proto %d [m %x] ]\n" + " [ src-ip IP-ADDRESS [m IP-ADDRESS] ]\n" + " [ dst-ip IP-ADDRESS [m IP-ADDRESS] ]\n" + " [ tos %d [m %x] ]\n" + " [ tclass %d [m %x] ]\n" + " [ l4proto %d [m %x] ]\n" + " [ src-port %d [m %x] ]\n" + " [ dst-port %d [m %x] ]\n" + " [ spi %d [m %x] ]\n" + " [ vlan-etype %x [m %x] ]\n" + " [ vlan %x [m %x] ]\n" + " [ user-def %x [m %x] ]\n" + " [ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" + " [ action %d ] | [ vf %d queue %d ]\n" + " [ context %d ]\n" + " [ loc %d ] |\n" + " delete %d\n" + }, + { + .opts = "-T|--show-time-stamping", + .func = do_tsinfo, + .nlfunc = nl_tsinfo, + .help = "Show time stamping capabilities" + }, + { + .opts = "-x|--show-rxfh-indir|--show-rxfh", + .json = true, + .func = do_grxfh, + .nlfunc = nl_grss, + .help = "Show Rx flow hash indirection table and/or RSS hash key", + .xhelp = " [ context %d ]\n" + }, + { + .opts = "-X|--set-rxfh-indir|--rxfh", + .func = do_srxfh, + .help = "Set Rx flow hash indirection table and/or RSS hash key", + .xhelp = " [ context %d|new ]\n" + " [ equal N | weight W0 W1 ... | default ]\n" + " [ hkey %x:%x:%x:%x:%x:.... ]\n" + " [ hfunc FUNC ]\n" + " [ delete ]\n" + }, + { + .opts = "-f|--flash", + .func = do_flash, + .help = "Flash firmware image from the specified file to a region on the device", + .xhelp = " FILENAME [ REGION-NUMBER-TO-FLASH ]\n" + }, + { + .opts = "-P|--show-permaddr", + .func = do_permaddr, + .nlfunc = nl_permaddr, + .help = "Show permanent hardware address" + }, + { + .opts = "-w|--get-dump", + .func = do_getfwdump, + .help = "Get dump flag, data", + .xhelp = " [ data FILENAME ]\n" + }, + { + .opts = "-W|--set-dump", + .func = do_setfwdump, + .help = "Set dump flag of the device", + .xhelp = " N\n" + }, + { + .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" + " [ other N ]\n" + " [ combined N ]\n" + }, + { + .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" + }, + { + .opts = "-m|--dump-module-eeprom|--module-info", + .func = do_getmodule, + .nlfunc = nl_getmodule, + .help = "Query/Decode Module EEPROM information and optical diagnostics if available", + .xhelp = " [ raw on|off ]\n" + " [ hex on|off ]\n" + " [ offset N ]\n" + " [ length N ]\n" + " [ page N ]\n" + " [ bank N ]\n" + " [ i2c N ]\n" + }, + { + .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" + " [ tx-lpi on|off ]\n" + " [ tx-timer %d ]\n" + }, + { + .opts = "--set-phy-tunable", + .func = do_set_phy_tunable, + .help = "Set PHY tunable", + .xhelp = " [ downshift on|off [count N] ]\n" + " [ fast-link-down on|off [msecs N] ]\n" + " [ energy-detect-power-down on|off [msecs N] ]\n" + }, + { + .opts = "--get-phy-tunable", + .func = do_get_phy_tunable, + .help = "Get PHY tunable", + .xhelp = " [ downshift ]\n" + " [ fast-link-down ]\n" + " [ energy-detect-power-down ]\n" + }, + { + .opts = "--get-tunable", + .func = do_gtunable, + .help = "Get tunable", + .xhelp = " [ rx-copybreak ]\n" + " [ tx-copybreak ]\n" + " [ tx-buf-size ]\n" + " [ pfc-prevention-tout ]\n" + }, + { + .opts = "--set-tunable", + .func = do_stunable, + .help = "Set tunable", + .xhelp = " [ rx-copybreak N ]\n" + " [ tx-copybreak N ]\n" + " [ tx-buf-size N ]\n" + " [ pfc-prevention-tout N ]\n" + }, + { + .opts = "--reset", + .func = do_reset, + .help = "Reset components", + .xhelp = " [ flags %x ]\n" + " [ mgmt ]\n" + " [ mgmt-shared ]\n" + " [ irq ]\n" + " [ irq-shared ]\n" + " [ dma ]\n" + " [ dma-shared ]\n" + " [ filter ]\n" + " [ filter-shared ]\n" + " [ offload ]\n" + " [ offload-shared ]\n" + " [ mac ]\n" + " [ mac-shared ]\n" + " [ phy ]\n" + " [ phy-shared ]\n" + " [ ram ]\n" + " [ ram-shared ]\n" + " [ ap ]\n" + " [ ap-shared ]\n" + " [ dedicated ]\n" + " [ all ]\n" + }, + { + .opts = "--show-fec", + .json = true, + .func = do_gfec, + .nlfunc = nl_gfec, + .help = "Show FEC settings", + }, + { + .opts = "--set-fec", + .func = do_sfec, + .nlfunc = nl_sfec, + .help = "Set FEC settings", + .xhelp = " [ encoding auto|off|rs|baser|llrs [...] ]\n" + }, + { + .opts = "-Q|--per-queue", + .func = do_perqueue, + .help = "Apply per-queue command. ", + .xhelp = "The supported sub commands include --show-coalesce, --coalesce" + " [queue_mask %x] SUB_COMMAND\n", + }, + { + .opts = "--cable-test", + .json = true, + .nlfunc = nl_cable_test, + .help = "Perform a cable test", + }, + { + .opts = "--cable-test-tdr", + .json = true, + .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 = "--show-module", + .json = true, + .nlfunc = nl_gmodule, + .help = "Show transceiver module settings", + }, + { + .opts = "--set-module", + .nlfunc = nl_smodule, + .help = "Set transceiver module settings", + .xhelp = " [ power-mode-policy high|auto ]\n" + }, + { + .opts = "--get-plca-cfg", + .nlfunc = nl_plca_get_cfg, + .help = "Get PLCA configuration", + }, + { + .opts = "--set-plca-cfg", + .nlfunc = nl_plca_set_cfg, + .help = "Set PLCA configuration", + .xhelp = " [ enable on|off ]\n" + " [ node-id N ]\n" + " [ node-cnt N ]\n" + " [ to-tmr N ]\n" + " [ burst-cnt N ]\n" + " [ burst-tmr N ]\n" + }, + { + .opts = "--get-plca-status", + .nlfunc = nl_plca_get_status, + .help = "Get PLCA status information", + }, + { + .opts = "--show-mm", + .json = true, + .nlfunc = nl_get_mm, + .help = "Show MAC merge layer state", + }, + { + .opts = "--set-mm", + .nlfunc = nl_set_mm, + .help = "Set MAC merge layer parameters", + " [ verify-enabled on|off ]\n" + " [ verify-time N ]\n" + " [ tx-enabled on|off ]\n" + " [ pmac-enabled on|off ]\n" + " [ tx-min-frag-size 60-252 ]\n" + }, + { + .opts = "--show-pse", + .json = true, + .nlfunc = nl_gpse, + .help = "Show settings for Power Sourcing Equipment", + }, + { + .opts = "--set-pse", + .nlfunc = nl_spse, + .help = "Set Power Sourcing Equipment settings", + .xhelp = " [ podl-pse-admin-control enable|disable ]\n" + }, + { + .opts = "-h|--help", + .no_dev = true, + .func = show_usage, + .help = "Show this help" + }, + { + .opts = "--version", + .no_dev = true, + .func = do_version, + .help = "Show version number" + }, {} }; -static int show_usage(struct cmd_context *ctx maybe_unused) +static int show_usage(struct cmd_context *ctx __maybe_unused) { int i; /* ethtool -h */ fprintf(stdout, PACKAGE " version " VERSION "\n"); - fprintf(stdout, - "Usage:\n" - " ethtool DEVNAME\t" - "Display standard information about device\n"); + fprintf(stdout, "Usage:\n"); for (i = 0; args[i].opts; i++) { - fputs(" ethtool ", stdout); + fputs(" ethtool [ FLAGS ] ", stdout); fprintf(stdout, "%s %s\t%s\n", args[i].opts, - args[i].want_device ? "DEVNAME" : "\t", + args[i].no_dev ? "\t" : "DEVNAME", args[i].help); - if (args[i].opthelp) - fputs(args[i].opthelp, stdout); + if (args[i].xhelp) + 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; } @@ -5514,7 +6180,7 @@ static int find_option(char *arg) size_t len; int k; - for (k = 0; args[k].opts; k++) { + for (k = 1; args[k].opts; k++) { opt = args[k].opts; for (;;) { len = strcspn(opt, "|"); @@ -5647,6 +6313,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, @@ -5699,67 +6367,121 @@ static int do_perqueue(struct cmd_context *ctx) return 0; } +static int ioctl_init(struct cmd_context *ctx, bool no_dev) +{ + if (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)); + strcpy(ctx->ifr.ifr_name, ctx->devname); + + /* Open control socket. */ + ctx->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ctx->fd < 0) + ctx->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (ctx->fd < 0) { + perror("Cannot get control socket"); + return 70; + } + + return 0; +} + int main(int argc, char **argp) { - int (*func)(struct cmd_context *); - int want_device; - struct cmd_context ctx; + struct cmd_context ctx = {}; + int ret; int k; init_global_link_mode_masks(); + if (argc < 2) + exit_bad_args(); + /* Skip command name */ argp++; argc--; + 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(); + + 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; + } + if (*argp && !strcmp(*argp, "--monitor")) { + ctx.argp = ++argp; + ctx.argc = --argc; + ret = nl_monitor(&ctx); + return ret ? 1 : 0; + } + /* First argument must be either a valid option or a device * name to get settings for (which we don't expect to begin * with '-'). */ - if (argc == 0) + if (!*argp) exit_bad_args(); k = find_option(*argp); - if (k >= 0) { + if (k > 0) { argp++; argc--; - func = args[k].func; - want_device = args[k].want_device; - goto opt_found; + } else { + if ((*argp)[0] == '-') + exit_bad_args(); + k = 0; } - if ((*argp)[0] == '-') - exit_bad_args(); - func = do_gset; - want_device = 1; -opt_found: - if (want_device) { + if (!args[k].no_dev) { ctx.devname = *argp++; argc--; - if (ctx.devname == NULL) + if (!ctx.devname) exit_bad_args(); - if (strlen(ctx.devname) >= IFNAMSIZ) - exit_bad_args(); - - /* Setup our control structures. */ - memset(&ctx.ifr, 0, sizeof(ctx.ifr)); - strcpy(ctx.ifr.ifr_name, ctx.devname); - - /* Open control socket. */ - ctx.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (ctx.fd < 0) - ctx.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); - if (ctx.fd < 0) { - perror("Cannot get control socket"); - return 70; - } - } else { - ctx.fd = -1; } - + if (ctx.json && !args[k].json) + exit_bad_args(); ctx.argc = argc; ctx.argp = argp; + netlink_run_handler(&ctx, args[k].nlchk, args[k].nlfunc, !args[k].func); + + if (ctx.json) /* no IOCTL command supports JSON output */ + exit_bad_args(); + + ret = ioctl_init(&ctx, args[k].no_dev); + if (ret) + return ret; - return func(&ctx); + return args[k].func(&ctx); } 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 @@ -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) { @@ -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; @@ -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/fsl_enetc.c b/fsl_enetc.c new file mode 100644 index 0000000..1180a66 --- /dev/null +++ b/fsl_enetc.c @@ -0,0 +1,259 @@ +/* Code to dump registers for the Freescale/NXP ENETC controller. + * + * Copyright 2022 NXP + */ +#include <stdio.h> +#include "internal.h" + +#define BIT(x) (1U << (x)) + +enum enetc_bdr_type {TX, RX}; +#define ENETC_SIMR 0 +#define ENETC_SIPMAR0 0x80 +#define ENETC_SIPMAR1 0x84 +#define ENETC_SICBDRMR 0x800 +#define ENETC_SICBDRSR 0x804 +#define ENETC_SICBDRBAR0 0x810 +#define ENETC_SICBDRBAR1 0x814 +#define ENETC_SICBDRPIR 0x818 +#define ENETC_SICBDRCIR 0x81c +#define ENETC_SICBDRLENR 0x820 +#define ENETC_SICAPR0 0x900 +#define ENETC_SICAPR1 0x904 +#define ENETC_SIUEFDCR 0xe28 + +#define ENETC_BDR_OFF(i) ((i) * 0x200) +#define ENETC_BDR(t, i, r) (0x8000 + (t) * 0x100 + ENETC_BDR_OFF(i) + (r)) + +/* RX BDR reg offsets */ +#define ENETC_RBMR 0 +#define ENETC_RBSR 0x4 +#define ENETC_RBBSR 0x8 +#define ENETC_RBCIR 0xc +#define ENETC_RBBAR0 0x10 +#define ENETC_RBBAR1 0x14 +#define ENETC_RBPIR 0x18 +#define ENETC_RBLENR 0x20 +#define ENETC_RBIER 0xa0 +#define ENETC_RBICR0 0xa8 +#define ENETC_RBICR1 0xac + +/* TX BDR reg offsets */ +#define ENETC_TBMR 0 +#define ENETC_TBSR 0x4 +#define ENETC_TBBAR0 0x10 +#define ENETC_TBBAR1 0x14 +#define ENETC_TBPIR 0x18 +#define ENETC_TBCIR 0x1c +#define ENETC_TBLENR 0x20 +#define ENETC_TBIER 0xa0 +#define ENETC_TBIDR 0xa4 +#define ENETC_TBICR0 0xa8 +#define ENETC_TBICR1 0xac + +/* Port registers */ +#define ENETC_PORT_BASE 0x10000 +#define ENETC_PMR ENETC_PORT_BASE + 0x0000 +#define ENETC_PSR ENETC_PORT_BASE + 0x0004 +#define ENETC_PSIPMR ENETC_PORT_BASE + 0x0018 +#define ENETC_PSIPMAR0(n) ENETC_PORT_BASE + (0x0100 + (n) * 0x8) /* n = SI index */ +#define ENETC_PSIPMAR1(n) ENETC_PORT_BASE + (0x0104 + (n) * 0x8) +#define ENETC_PTXMBAR ENETC_PORT_BASE + 0x0608 +#define ENETC_PCAPR0 ENETC_PORT_BASE + 0x0900 +#define ENETC_PCAPR1 ENETC_PORT_BASE + 0x0904 +#define ENETC_PSICFGR0(n) ENETC_PORT_BASE + (0x0940 + (n) * 0xc) /* n = SI index */ + +#define ENETC_PRFSCAPR ENETC_PORT_BASE + 0x1804 +#define ENETC_PTCMSDUR(n) ENETC_PORT_BASE + (0x2020 + (n) * 4) /* n = TC index [0..7] */ + +#define ENETC_PM0_CMD_CFG ENETC_PORT_BASE + 0x8008 +#define ENETC_PM0_CMD_TX_EN BIT(0) +#define ENETC_PM0_CMD_RX_EN BIT(1) +#define ENETC_PM0_CMD_WAN BIT(3) +#define ENETC_PM0_CMD_PROMISC BIT(4) +#define ENETC_PM0_CMD_PAD BIT(5) +#define ENETC_PM0_CMD_CRC BIT(6) +#define ENETC_PM0_CMD_PAUSE_FWD BIT(7) +#define ENETC_PM0_CMD_PAUSE_IGN BIT(8) +#define ENETC_PM0_CMD_TX_ADDR_INS BIT(9) +#define ENETC_PM0_CMD_XGLP BIT(10) +#define ENETC_PM0_CMD_TXP BIT(11) +#define ENETC_PM0_CMD_SWR BIT(12) +#define ENETC_PM0_CMD_CNT_FRM_EN BIT(13) +#define ENETC_PM0_CMD_SEND_IDLE BIT(16) +#define ENETC_PM0_CMD_NO_LEN_CHK BIT(17) +#define ENETC_PM0_CMD_SFD BIT(21) +#define ENETC_PM0_CMD_TX_LOWP_ENA BIT(23) +#define ENETC_PM0_CMD_REG_LOWP_RXETY BIT(24) +#define ENETC_PM0_CMD_RXSTP BIT(29) +#define ENETC_PM0_CMD_MG BIT(31) + +#define ENETC_PM0_MAXFRM ENETC_PORT_BASE + 0x8014 +#define ENETC_PM0_IF_MODE ENETC_PORT_BASE + 0x8300 + +struct enetc_register { + u32 addr; + const char *name; + void (*decode)(u32 val, char *buf); +}; + +#define REG(_reg, _name) { .addr = (_reg), .name = (_name) } + +#define REG_DEC(_reg, _name, _decode) \ + { .addr = (_reg), .name = (_name), .decode = (_decode) } + +static void decode_cmd_cfg(u32 val, char *buf) +{ + sprintf(buf, "\tMG %d\n\tRXSTP %d\n\tREG_LOWP_RXETY %d\n" + "\tTX_LOWP_ENA %d\n\tSFD %d\n\tNO_LEN_CHK %d\n\tSEND_IDLE %d\n" + "\tCNT_FRM_EN %d\n\tSWR %d\n\tTXP %d\n\tXGLP %d\n" + "\tTX_ADDR_INS %d\n\tPAUSE_IGN %d\n\tPAUSE_FWD %d\n\tCRC %d\n" + "\tPAD %d\n\tPROMIS %d\n\tWAN %d\n\tRX_EN %d\n\tTX_EN %d\n", + !!(val & ENETC_PM0_CMD_MG), + !!(val & ENETC_PM0_CMD_RXSTP), + !!(val & ENETC_PM0_CMD_REG_LOWP_RXETY), + !!(val & ENETC_PM0_CMD_TX_LOWP_ENA), + !!(val & ENETC_PM0_CMD_SFD), + !!(val & ENETC_PM0_CMD_NO_LEN_CHK), + !!(val & ENETC_PM0_CMD_SEND_IDLE), + !!(val & ENETC_PM0_CMD_CNT_FRM_EN), + !!(val & ENETC_PM0_CMD_SWR), + !!(val & ENETC_PM0_CMD_TXP), + !!(val & ENETC_PM0_CMD_XGLP), + !!(val & ENETC_PM0_CMD_TX_ADDR_INS), + !!(val & ENETC_PM0_CMD_PAUSE_IGN), + !!(val & ENETC_PM0_CMD_PAUSE_FWD), + !!(val & ENETC_PM0_CMD_CRC), + !!(val & ENETC_PM0_CMD_PAD), + !!(val & ENETC_PM0_CMD_PROMISC), + !!(val & ENETC_PM0_CMD_WAN), + !!(val & ENETC_PM0_CMD_RX_EN), + !!(val & ENETC_PM0_CMD_TX_EN)); +} + +#define RXBDR_REGS(_i) \ + REG(ENETC_BDR(RX, (_i), ENETC_RBMR), "RX BDR " #_i " mode register"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBSR), "RX BDR " #_i " status register"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBBSR), "RX BDR " #_i " buffer size register"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBPIR), "RX BDR " #_i " producer index register"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBCIR), "RX BDR " #_i " consumer index register"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBBAR0), "RX BDR " #_i " base address register 0"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBBAR1), "RX BDR " #_i " base address register 1"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBLENR), "RX BDR " #_i " length register"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBIER), "RX BDR " #_i " interrupt enable register"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBICR0), "RX BDR " #_i " interrupt coalescing register 0"), \ + REG(ENETC_BDR(RX, (_i), ENETC_RBICR1), "RX BDR " #_i " interrupt coalescing register 1") + +#define TXBDR_REGS(_i) \ + REG(ENETC_BDR(TX, (_i), ENETC_TBMR), "TX BDR " #_i " mode register"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBSR), "TX BDR " #_i " status register"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBBAR0), "TX BDR " #_i " base address register 0"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBBAR1), "TX BDR " #_i " base address register 1"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBPIR), "TX BDR " #_i " producer index register"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBCIR), "TX BDR " #_i " consumer index register"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBLENR), "TX BDR " #_i " length register"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBIER), "TX BDR " #_i " interrupt enable register"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBICR0), "TX BDR " #_i " interrupt coalescing register 0"), \ + REG(ENETC_BDR(TX, (_i), ENETC_TBICR1), "TX BDR " #_i " interrupt coalescing register 1") + +static const struct enetc_register known_enetc_regs[] = { + REG(ENETC_SIMR, "SI mode register"), + REG(ENETC_SIPMAR0, "SI primary MAC address register 0"), + REG(ENETC_SIPMAR1, "SI primary MAC address register 1"), + REG(ENETC_SICBDRMR, "SI control BDR mode register"), + REG(ENETC_SICBDRSR, "SI control BDR status register"), + REG(ENETC_SICBDRBAR0, "SI control BDR base address register 0"), + REG(ENETC_SICBDRBAR1, "SI control BDR base address register 1"), + REG(ENETC_SICBDRPIR, "SI control BDR producer index register"), + REG(ENETC_SICBDRCIR, "SI control BDR consumer index register"), + REG(ENETC_SICBDRLENR, "SI control BDR length register"), + REG(ENETC_SICAPR0, "SI capability register 0"), + REG(ENETC_SICAPR1, "SI capability register 1"), + REG(ENETC_SIUEFDCR, "SI uncorrectable error frame drop count register"), + + TXBDR_REGS(0), TXBDR_REGS(1), TXBDR_REGS(2), TXBDR_REGS(3), + TXBDR_REGS(4), TXBDR_REGS(5), TXBDR_REGS(6), TXBDR_REGS(7), + TXBDR_REGS(8), TXBDR_REGS(9), TXBDR_REGS(10), TXBDR_REGS(11), + TXBDR_REGS(12), TXBDR_REGS(13), TXBDR_REGS(14), TXBDR_REGS(15), + + RXBDR_REGS(0), RXBDR_REGS(1), RXBDR_REGS(2), RXBDR_REGS(3), + RXBDR_REGS(4), RXBDR_REGS(5), RXBDR_REGS(6), RXBDR_REGS(7), + RXBDR_REGS(8), RXBDR_REGS(9), RXBDR_REGS(10), RXBDR_REGS(11), + RXBDR_REGS(12), RXBDR_REGS(13), RXBDR_REGS(14), RXBDR_REGS(15), + + REG(ENETC_PMR, "Port mode register"), + REG(ENETC_PSR, "Port status register"), + REG(ENETC_PSIPMR, "Port SI promiscuous mode register"), + REG(ENETC_PSIPMAR0(0), "Port SI0 primary MAC address register 0"), + REG(ENETC_PSIPMAR1(0), "Port SI0 primary MAC address register 1"), + REG(ENETC_PTXMBAR, "Port HTA transmit memory buffer allocation register"), + REG(ENETC_PCAPR0, "Port capability register 0"), + REG(ENETC_PCAPR1, "Port capability register 1"), + REG(ENETC_PSICFGR0(0), "Port SI0 configuration register 0"), + REG(ENETC_PRFSCAPR, "Port RFS capability register"), + REG(ENETC_PTCMSDUR(0), "Port traffic class 0 maximum SDU register"), + REG_DEC(ENETC_PM0_CMD_CFG, "Port eMAC Command and Configuration Register", + decode_cmd_cfg), + REG(ENETC_PM0_MAXFRM, "Port eMAC Maximum Frame Length Register"), + REG(ENETC_PM0_IF_MODE, "Port eMAC Interface Mode Control Register"), +}; + +static void decode_known_reg(const struct enetc_register *reg, u32 val) +{ + char buf[512]; + + reg->decode(val, buf); + fprintf(stdout, "%s: 0x%x\n%s", reg->name, val, buf); +} + +static void dump_known_reg(const struct enetc_register *reg, u32 val) +{ + fprintf(stdout, "%s: 0x%x\n", reg->name, val); +} + +static void dump_unknown_reg(u32 addr, u32 val) +{ + fprintf(stdout, "Reg 0x%x: 0x%x\n", addr, val); +} + +static void dump_reg(u32 addr, u32 val) +{ + const struct enetc_register *reg; + u32 i; + + for (i = 0; i < ARRAY_SIZE(known_enetc_regs); i++) { + reg = &known_enetc_regs[i]; + if (reg->addr == addr) { + if (reg->decode) + decode_known_reg(reg, val); + else + dump_known_reg(reg, val); + return; + } + } + + dump_unknown_reg(addr, val); +} + +/* Registers are structured in an array of key/value u32 pairs. + * Compare each key to our list of known registers, or print it + * as a raw address otherwise. + */ +int fsl_enetc_dump_regs(struct ethtool_drvinfo *info __maybe_unused, + struct ethtool_regs *regs) +{ + u32 *data = (u32 *)regs->data; + u32 len = regs->len; + + if (len % 8) { + fprintf(stdout, "Expected length to be multiple of 8 bytes\n"); + return -1; + } + + while (len) { + dump_reg(data[0], data[1]); + data += 2; len -= 8; + } + + return 0; +} @@ -0,0 +1,829 @@ +/* Copyright (c) 2023 Huawei Corporation */ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include "internal.h" + +/* distinguish drive register data of earlier versions */ +#define HNS3_REG_MAGIC_NUMBER 0x686e733372656773 /* hns3regs */ +#define HNS3_REG_RSV_NAME "reserved" +#define HNS3_REG_UNKNOW_NAME "unknown" +#define HNS3_REG_UNKNOW_VALUE_LEN 4 + +struct hns3_reg_tlv { + u16 tag; + u16 len; +}; + +struct hns3_reg_header { + u64 magic_number; + u8 is_vf; + u8 rsv[7]; +}; + +struct hns3_reg_info { + const char *name; + u16 value_len; +}; + +struct hns3_regs_group { + const char *group_name; + const struct hns3_reg_info *regs; + u16 regs_count; +}; + +enum hns3_reg_tag { + HNS3_TAG_CMDQ = 0, + HNS3_TAG_COMMON, + HNS3_TAG_RING, + HNS3_TAG_TQP_INTR, + HNS3_TAG_QUERY_32_BIT, + HNS3_TAG_QUERY_64_BIT, + HNS3_TAG_DFX_BIOS_COMMON, + HNS3_TAG_DFX_SSU_0, + HNS3_TAG_DFX_SSU_1, + HNS3_TAG_DFX_IGU_EGU, + HNS3_TAG_DFX_RPU_0, + HNS3_TAG_DFX_RPU_1, + HNS3_TAG_DFX_NCSI, + HNS3_TAG_DFX_RTC, + HNS3_TAG_DFX_PPP, + HNS3_TAG_DFX_RCB, + HNS3_TAG_DFX_TQP, + HNS3_TAG_DFX_SSU_2, + HNS3_TAG_DFX_RPU_TNL, + HNS3_TAG_MAX, +}; + +const bool hns3_reg_is_repeat_tag_array[] = { + [HNS3_TAG_RING] = true, + [HNS3_TAG_TQP_INTR] = true, + [HNS3_TAG_DFX_RPU_TNL] = true, +}; + +const struct hns3_reg_info pf_cmdq_regs[] = { + {"comm_nic_csq_baseaddr_l", 4}, + {"comm_nic_csq_baseaddr_h", 4}, + {"comm_nic_csq_depth", 4}, + {"comm_nic_csq_tail", 4}, + {"comm_nic_csq_head", 4}, + {"comm_nic_crq_baseaddr_l", 4}, + {"comm_nic_crq_baseaddr_h", 4}, + {"comm_nic_crq_depth", 4}, + {"comm_nic_crq_tail", 4}, + {"comm_nic_crq_head", 4}, + {"comm_vector0_cmdq_src", 4}, + {"comm_cmdq_intr_sts", 4}, + {"comm_cmdq_intr_en", 4}, + {"comm_cmdq_intr_gen", 4}, +}; + +const struct hns3_reg_info pf_common_regs[] = { + {"misc_vector_base", 4}, + {"pf_other_int", 4}, + {"misc_reset_sts", 4}, + {"misc_vector_int_sts", 4}, + {"global_reset", 4}, + {"fun_rst_ing", 4}, + {"gro_en", 4}, +}; + +const struct hns3_reg_info pf_ring_regs[] = { + {"ring_rx_addr_l", 4}, + {"ring_rx_addr_h", 4}, + {"ring_rx_bd_num", 4}, + {"ring_rx_bd_length", 4}, + {"ring_rx_merge_en", 4}, + {"ring_rx_tail", 4}, + {"ring_rx_head", 4}, + {"ring_rx_fbd_num", 4}, + {"ring_rx_offset", 4}, + {"ring_rx_fbd_offset", 4}, + {"ring_rx_stash", 4}, + {"ring_rx_bd_err", 4}, + {"ring_tx_addr_l", 4}, + {"ring_tx_addr_h", 4}, + {"ring_tx_bd_num", 4}, + {"ring_tx_priority", 4}, + {"ring_tx_tc", 4}, + {"ring_tx_merge_en", 4}, + {"ring_tx_tail", 4}, + {"ring_tx_head", 4}, + {"ring_tx_fbd_num", 4}, + {"ring_tx_offset", 4}, + {"ring_tx_ebd_num", 4}, + {"ring_tx_ebd_offset", 4}, + {"ring_tx_bd_err", 4}, + {"ring_en", 4}, +}; + +const struct hns3_reg_info pf_tqp_intr_regs[] = { + {"tqp_intr_ctrl", 4}, + {"tqp_intr_gl0", 4}, + {"tqp_intr_gl1", 4}, + {"tqp_intr_gl2", 4}, + {"tqp_intr_rl", 4}, +}; + +const struct hns3_reg_info query_32_bit_regs[] = { + {"ssu_common_err_int", 4}, + {"ssu_port_based_err_int", 4}, + {"ssu_fifo_overflow_int", 4}, + {"ssu_ets_tcg_int", 4}, + {"ssu_bp_status_0", 4}, + {"ssu_bp_status_1", 4}, + {"ssu_bp_status_2", 4}, + {"ssu_bp_status_3", 4}, + {"ssu_bp_status_4", 4}, + {"ssu_bp_status_5", 4}, + {"ssu_mac_tx_pfc_ind", 4}, + {"ssu_mac_rx_pfc_ind", 4}, + {"ssu_rx_oq_drop_pkt_cnt", 4}, + {"ssu_tx_oq_drop_pkt_cnt", 4}, +}; + +const struct hns3_reg_info query_64_bit_regs[] = { + {"ppp_get_rx_pkt_cnt", 8}, + {"ppp_get_tx_pkt_cnt", 8}, + {"ppp_send_uc_prt2host_pkt_cnt", 8}, + {"ppp_send_uc_prt2prt_pkt_cnt", 8}, + {"ppp_send_uc_host2host_pkt_cnt", 8}, + {"ppp_send_uc_host2prt_pkt_cnt", 8}, + {"ppp_send_mc_from_prt_cnt", 8}, +}; + +const struct hns3_reg_info dfx_bios_common_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"bp_cpu_state", 4}, + {"dfx_msix_info_nic_0", 4}, + {"dfx_msix_info_nic_1", 4}, + {"dfx_msix_info_nic_2", 4}, + {"dfx_msix_info_nic_3", 4}, + {"dfx_msix_info_roc_0", 4}, + {"dfx_msix_info_roc_1", 4}, + {"dfx_msix_info_roc_2", 4}, + {"dfx_msix_info_roc_3", 4}, + {HNS3_REG_RSV_NAME, 8}, +}; + +const struct hns3_reg_info dfx_ssu_0_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"ssu_ets_port_status", 4}, + {"ssu_ets_tcg_status", 4}, + {HNS3_REG_RSV_NAME, 4}, + {HNS3_REG_RSV_NAME, 4}, + {"ssu_bp_status_0", 4}, + {"ssu_bp_status_1", 4}, + {"ssu_bp_status_2", 4}, + {"ssu_bp_status_3", 4}, + {"ssu_bp_status_4", 4}, + {"ssu_bp_status_5", 4}, + {"ssu_mac_tx_pfc_ind", 4}, + {"mac_ssu_rx_pfc_ind", 4}, + {"btmp_ageing_st_b0", 4}, + {"btmp_ageing_st_b1", 4}, + {"btmp_ageing_st_b2", 4}, + {HNS3_REG_RSV_NAME, 8}, + {"full_drop_num", 4}, + {"part_drop_num", 4}, + {"ppp_key_drop_num", 4}, + {"ppp_rlt_drop_num", 4}, + {"lo_pri_unicast_rlt_drop_num", 4}, + {"hi_pri_multicast_rlt_drop_num", 4}, + {"lo_pri_multicast_rlt_drop_num", 4}, + {"ncsi_packet_curr_buffer_cnt", 4}, + {HNS3_REG_RSV_NAME, 12}, + {"ssu_mb_rd_rlt_drop_cnt", 4}, + {"ssu_ppp_mac_key_num", 8}, + {"ssu_ppp_host_key_num", 8}, + {"ppp_ssu_mac_rlt_num", 8}, + {"ppp_ssu_host_rlt_num", 8}, + {"ncsi_rx_packet_in_cnt", 8}, + {"ncsi_tx_packet_out_cnt", 8}, + {"ssu_key_drop_num", 4}, + {"mb_uncopy_num", 4}, + {"rx_oq_drop_pkt_cnt", 4}, + {"tx_oq_drop_pkt_cnt", 4}, + {"bank_unbalance_drop_cnt", 4}, + {"bank_unbalance_rx_drop_cnt", 4}, + {"nic_l2_err_drop_pkt_cnt", 4}, + {"roc_l2_err_drop_pkt_cnt", 4}, + {"nic_l2_err_drop_pkt_cnt_rx", 4}, + {"roc_l2_err_drop_pkt_cnt_rx", 4}, + {"rx_oq_glb_drop_pkt_cnt", 4}, + {HNS3_REG_RSV_NAME, 4}, + {"lo_pri_unicast_cur_cnt", 4}, + {"hi_pri_multicast_cur_cnt", 4}, + {"lo_pri_multicast_cur_cnt", 4}, + {HNS3_REG_RSV_NAME, 12}, +}; + +const struct hns3_reg_info dfx_ssu_1_regs[] = { + {"prt_id", 4}, + {"packet_tc_curr_buffer_cnt_0", 4}, + {"packet_tc_curr_buffer_cnt_1", 4}, + {"packet_tc_curr_buffer_cnt_2", 4}, + {"packet_tc_curr_buffer_cnt_3", 4}, + {"packet_tc_curr_buffer_cnt_4", 4}, + {"packet_tc_curr_buffer_cnt_5", 4}, + {"packet_tc_curr_buffer_cnt_6", 4}, + {"packet_tc_curr_buffer_cnt_7", 4}, + {"packet_curr_buffer_cnt", 4}, + {HNS3_REG_RSV_NAME, 8}, + {"rx_packet_in_cnt", 8}, + {"rx_packet_out_cnt", 8}, + {"tx_packet_in_cnt", 8}, + {"tx_packet_out_cnt", 8}, + {"roc_rx_packet_in_cnt", 8}, + {"roc_tx_packet_out_cnt", 8}, + {"rx_packet_tc_in_cnt_0", 8}, + {"rx_packet_tc_in_cnt_1", 8}, + {"rx_packet_tc_in_cnt_2", 8}, + {"rx_packet_tc_in_cnt_3", 8}, + {"rx_packet_tc_in_cnt_4", 8}, + {"rx_packet_tc_in_cnt_5", 8}, + {"rx_packet_tc_in_cnt_6", 8}, + {"rx_packet_tc_in_cnt_7", 8}, + {"rx_packet_tc_out_cnt_0", 8}, + {"rx_packet_tc_out_cnt_1", 8}, + {"rx_packet_tc_out_cnt_2", 8}, + {"rx_packet_tc_out_cnt_3", 8}, + {"rx_packet_tc_out_cnt_4", 8}, + {"rx_packet_tc_out_cnt_5", 8}, + {"rx_packet_tc_out_cnt_6", 8}, + {"rx_packet_tc_out_cnt_7", 8}, + {"tx_packet_tc_in_cnt_0", 8}, + {"tx_packet_tc_in_cnt_1", 8}, + {"tx_packet_tc_in_cnt_2", 8}, + {"tx_packet_tc_in_cnt_3", 8}, + {"tx_packet_tc_in_cnt_4", 8}, + {"tx_packet_tc_in_cnt_5", 8}, + {"tx_packet_tc_in_cnt_6", 8}, + {"tx_packet_tc_in_cnt_7", 8}, + {"tx_packet_tc_out_cnt_0", 8}, + {"tx_packet_tc_out_cnt_1", 8}, + {"tx_packet_tc_out_cnt_2", 8}, + {"tx_packet_tc_out_cnt_3", 8}, + {"tx_packet_tc_out_cnt_4", 8}, + {"tx_packet_tc_out_cnt_5", 8}, + {"tx_packet_tc_out_cnt_6", 8}, + {"tx_packet_tc_out_cnt_7", 8}, + {HNS3_REG_RSV_NAME, 8}, +}; + +const struct hns3_reg_info dfx_igu_egu_regs[] = { + {"prt_id", 4}, + {"igu_rx_err_pkt", 4}, + {"igu_rx_no_sof_pkt", 4}, + {"egu_tx_1588_short_pkt", 4}, + {"egu_tx_1588_pkt", 4}, + {"egu_tx_err_pkt", 4}, + {"igu_rx_out_l2_pkt", 4}, + {"igu_rx_out_l3_pkt", 4}, + {"igu_rx_out_l4_pkt", 4}, + {"igu_rx_in_l2_pkt", 4}, + {"igu_rx_in_l3_pkt", 4}, + {"igu_rx_in_l4_pkt", 4}, + {"igu_rx_el3e_pkt", 4}, + {"igu_rx_el4e_pkt", 4}, + {"igu_rx_l3e_pkt", 4}, + {"igu_rx_l4e_pkt", 4}, + {"igu_rx_rocee_pkt", 4}, + {"igu_rx_out_udp0_pkt", 4}, + {"igu_rx_in_udp0_pkt", 4}, + {"mul_car_drop_pkt_cnt", 8}, + {"bro_car_drop_pkt_cnt", 8}, + {HNS3_REG_RSV_NAME, 4}, + {"igu_rx_oversize_pkt", 8}, + {"igu_rx_undersize_pkt", 8}, + {"igu_rx_out_all_pkt", 8}, + {"igu_tx_out_all_pkt", 8}, + {"igu_rx_uni_pkt", 8}, + {"igu_rx_multi_pkt", 8}, + {"igu_rx_broad_pkt", 8}, + {"egu_tx_out_all_pkt", 8}, + {"egu_tx_uni_pkt", 8}, + {"egu_tx_multi_pkt", 8}, + {"egu_tx_broad_pkt", 8}, + {"igu_tx_key_num", 8}, + {"igu_rx_non_tun_pkt", 8}, + {"igu_rx_tun_pkt", 8}, + {HNS3_REG_RSV_NAME, 8}, +}; + +const struct hns3_reg_info dfx_rpu_0_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"fsm_dfx_st0", 4}, + {"fsm_dfx_st1", 4}, + {"rpu_rx_pkt_drop_cnt", 4}, + {"buf_wait_timeout", 4}, + {"buf_wait_timeout_qid", 4}, +}; + +const struct hns3_reg_info dfx_rpu_1_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"fifo_dfx_st0", 4}, + {"fifo_dfx_st1", 4}, + {"fifo_dfx_st2", 4}, + {"fifo_dfx_st3", 4}, + {"fifo_dfx_st4", 4}, + {"fifo_dfx_st5", 4}, + {HNS3_REG_RSV_NAME, 20}, +}; + +const struct hns3_reg_info dfx_ncsi_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"ncsi_egu_tx_fifo_sts", 4}, + {"ncsi_pause_status", 4}, + {"ncsi_rx_ctrl_dmac_err_cnt", 4}, + {"ncsi_rx_ctrl_smac_err_cnt", 4}, + {"ncsi_rx_ctrl_cks_err_cnt", 4}, + {"ncsi_rx_ctrl_pkt_cnt", 4}, + {"ncsi_rx_pt_dmac_err_cnt", 4}, + {"ncsi_rx_pt_smac_err_cnt", 4}, + {"ncsi_rx_pt_pkt_cnt", 4}, + {"ncsi_rx_fcs_err_cnt", 4}, + {"ncsi_tx_ctrl_dmac_err_cnt", 4}, + {"ncsi_tx_ctrl_smac_err_cnt", 4}, + {"ncsi_tx_ctrl_pkt_cnt", 4}, + {"ncsi_tx_pt_dmac_err_cnt", 4}, + {"ncsi_tx_pt_smac_err_cnt", 4}, + {"ncsi_tx_pt_pkt_cnt", 4}, + {"ncsi_tx_pt_pkt_trun_cnt", 4}, + {"ncsi_tx_pt_pkt_err_cnt", 4}, + {"ncsi_tx_ctrl_pkt_err_cnt", 4}, + {"ncsi_rx_ctrl_pkt_trun_cnt", 4}, + {"ncsi_rx_ctrl_pkt_cflit_cnt", 4}, + {HNS3_REG_RSV_NAME, 8}, + {"ncsi_mac_rx_octets_ok", 4}, + {"ncsi_mac_rx_octets_bad", 4}, + {"ncsi_mac_rx_uc_pkts", 4}, + {"ncsi_mac_rx_mc_pkts", 4}, + {"ncsi_mac_rx_bc_pkts", 4}, + {"ncsi_mac_rx_pkts_64octets", 4}, + {"ncsi_mac_rx_pkts_65to127octets", 4}, + {"ncsi_mac_rx_pkts_128to255octets", 4}, + {"ncsi_mac_rx_pkts_256to511octets", 4}, + {"ncsi_mac_rx_pkts_512to1023octets", 4}, + {"ncsi_mac_rx_pkts_1024to1518octets", 4}, + {"ncsi_mac_rx_pkts_1519tomaxoctets", 4}, + {"ncsi_mac_rx_fcs_errors", 4}, + {"ncsi_mac_rx_long_errors", 4}, + {"ncsi_mac_rx_jabber_errors", 4}, + {"ncsi_mac_rx_runt_err_cnt", 4}, + {"ncsi_mac_rx_short_err_cnt", 4}, + {"ncsi_mac_rx_filt_pkt_cnt", 4}, + {"ncsi_mac_rx_octets_total_filt", 4}, + {"ncsi_mac_tx_octets_ok", 4}, + {"ncsi_mac_tx_octets_bad", 4}, + {"ncsi_mac_tx_uc_pkts", 4}, + {"ncsi_mac_tx_mc_pkts", 4}, + {"ncsi_mac_tx_bc_pkts", 4}, + {"ncsi_mac_tx_pkts_64octets", 4}, + {"ncsi_mac_tx_pkts_65to127octets", 4}, + {"ncsi_mac_tx_pkts_128to255octets", 4}, + {"ncsi_mac_tx_pkts_256to511octets", 4}, + {"ncsi_mac_tx_pkts_512to1023octets", 4}, + {"ncsi_mac_tx_pkts_1024to1518octets", 4}, + {"ncsi_mac_tx_pkts_1519tomaxoctets", 4}, + {"ncsi_mac_tx_underrun", 4}, + {"ncsi_mac_tx_crc_error", 4}, + {"ncsi_mac_tx_pause_frames", 4}, + {"ncsi_mac_rx_pad_pkts", 4}, + {"ncsi_mac_rx_pause_frames", 4}, +}; + +const struct hns3_reg_info dfx_rtc_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"lge_igu_afifo_dfx_0", 4}, + {"lge_igu_afifo_dfx_1", 4}, + {"lge_igu_afifo_dfx_2", 4}, + {"lge_igu_afifo_dfx_3", 4}, + {"lge_igu_afifo_dfx_4", 4}, + {"lge_igu_afifo_dfx_5", 4}, + {"lge_igu_afifo_dfx_6", 4}, + {"lge_igu_afifo_dfx_7", 4}, + {"lge_egu_afifo_dfx_0", 4}, + {"lge_egu_afifo_dfx_1", 4}, + {"lge_egu_afifo_dfx_2", 4}, + {"lge_egu_afifo_dfx_3", 4}, + {"lge_egu_afifo_dfx_4", 4}, + {"lge_egu_afifo_dfx_5", 4}, + {"lge_egu_afifo_dfx_6", 4}, + {"lge_egu_afifo_dfx_7", 4}, + {"cge_igu_afifo_dfx_0", 4}, + {"cge_igu_afifo_dfx_1", 4}, + {"cge_egu_afifo_dfx_0", 4}, + {"cge_egu_afifo_dfx_1", 4}, + {HNS3_REG_RSV_NAME, 12}, +}; + +const struct hns3_reg_info dfx_ppp_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"drop_from_prt_pkt_cnt", 4}, + {"drop_from_host_pkt_cnt", 4}, + {"drop_tx_vlan_proc_cnt", 4}, + {"drop_mng_cnt", 4}, + {"drop_fd_cnt", 4}, + {"drop_no_dst_cnt", 4}, + {"drop_mc_mbid_full_cnt", 4}, + {"drop_sc_filtered", 4}, + {"ppp_mc_drop_pkt_cnt", 4}, + {"drop_pt_cnt", 4}, + {"drop_mac_anti_spoof_cnt", 4}, + {"drop_ig_vfv_cnt", 4}, + {"drop_ig_prtv_cnt", 4}, + {"drop_cnm_pfc_pause_cnt", 4}, + {"drop_torus_tc_cnt", 4}, + {"drop_torus_lpbk_cnt", 4}, + {"ppp_hfs_sts", 4}, + {"ppp_mc_rslt_sts", 4}, + {"ppp_p3u_sts", 4}, + {HNS3_REG_RSV_NAME, 4}, + {"ppp_umv_sts_0", 4}, + {"ppp_umv_sts_1", 4}, + {"ppp_vfv_sts", 4}, + {"ppp_gro_key_cnt", 4}, + {"ppp_gro_info_cnt", 4}, + {"ppp_gro_drop_cnt", 4}, + {"ppp_gro_out_cnt", 4}, + {"ppp_gro_key_match_data_cnt", 4}, + {"ppp_gro_key_match_tcam_cnt", 4}, + {"ppp_gro_info_match_cnt", 4}, + {"ppp_gro_free_entry_cnt", 4}, + {"ppp_gro_inner_dfx_signal", 4}, + {HNS3_REG_RSV_NAME, 12}, + {"get_rx_pkt_cnt", 8}, + {"get_tx_pkt_cnt", 8}, + {"send_uc_prt2host_pkt_cnt", 8}, + {"send_uc_prt2prt_pkt_cnt", 8}, + {"send_uc_host2host_pkt_cnt", 8}, + {"send_uc_host2prt_pkt_cnt", 8}, + {"send_mc_from_prt_cnt", 8}, + {"send_mc_from_host_cnt", 8}, + {"ssu_mc_rd_cnt", 8}, + {"ssu_mc_drop_cnt", 8}, + {"ssu_mc_rd_pkt_cnt", 8}, + {"ppp_mc_2host_pkt_cnt", 8}, + {"ppp_mc_2prt_pkt_cnt", 8}, + {"ntsnos_pkt_cnt", 8}, + {"ntup_pkt_cnt", 8}, + {"ntlcl_pkt_cnt", 8}, + {"nttgt_pkt_cnt", 8}, + {"rtns_pkt_cnt", 8}, + {"rtlpbk_pkt_cnt", 8}, + {"nr_pkt_cnt", 8}, + {"rr_pkt_cnt", 8}, + {"mng_tbl_hit_cnt", 8}, + {"fd_tbl_hit_cnt", 8}, + {"fd_lkup_cnt", 8}, + {"bc_hit_cnt", 8}, + {"um_tbl_uc_hit_cnt", 8}, + {"um_tbl_mc_hit_cnt", 8}, + {"um_tbl_snq_hit_cnt", 8}, + {HNS3_REG_RSV_NAME, 8}, + {"fwd_bonding_hit_cnt", 8}, + {"promis_tbl_hit_cnt", 8}, + {"get_tunl_pkt_cnt", 8}, + {"get_bmc_pkt_cnt", 8}, + {"send_uc_prt2bmc_pkt_cnt", 8}, + {"send_uc_host2bmc_pkt_cnt", 8}, + {"send_uc_bmc2host_pkt_cnt", 8}, + {"send_uc_bmc2prt_pkt_cnt", 8}, + {"ppp_mc_2bmc_pkt_cnt", 8}, + {HNS3_REG_RSV_NAME, 24}, + {"rx_default_host_hit_cnt", 8}, + {"lan_pair_cnt", 8}, + {"um_tbl_mc_hit_pkt_cnt", 8}, + {"mta_tbl_hit_pkt_cnt", 8}, + {"promis_tbl_hit_pkt_cnt", 8}, + {HNS3_REG_RSV_NAME, 16}, +}; + +const struct hns3_reg_info dfx_rcb_regs[] = { + {HNS3_REG_RSV_NAME, 4}, + {"fsm_dfx_st0", 4}, + {"fsm_dfx_st1", 4}, + {"fsm_dfx_st2", 4}, + {"fifo_dfx_st0", 4}, + {"fifo_dfx_st1", 4}, + {"fifo_dfx_st2", 4}, + {"fifo_dfx_st3", 4}, + {"fifo_dfx_st4", 4}, + {"fifo_dfx_st5", 4}, + {"fifo_dfx_st6", 4}, + {"fifo_dfx_st7", 4}, + {"fifo_dfx_st8", 4}, + {"fifo_dfx_st9", 4}, + {"fifo_dfx_st10", 4}, + {"fifo_dfx_st11", 4}, + {"q_credit_vld_0", 4}, + {"q_credit_vld_1", 4}, + {"q_credit_vld_2", 4}, + {"q_credit_vld_3", 4}, + {"q_credit_vld_4", 4}, + {"q_credit_vld_5", 4}, + {"q_credit_vld_6", 4}, + {"q_credit_vld_7", 4}, + {"q_credit_vld_8", 4}, + {"q_credit_vld_9", 4}, + {"q_credit_vld_10", 4}, + {"q_credit_vld_11", 4}, + {"q_credit_vld_12", 4}, + {"q_credit_vld_13", 4}, + {"q_credit_vld_14", 4}, + {"q_credit_vld_15", 4}, + {"q_credit_vld_16", 4}, + {"q_credit_vld_17", 4}, + {"q_credit_vld_18", 4}, + {"q_credit_vld_19", 4}, + {"q_credit_vld_20", 4}, + {"q_credit_vld_21", 4}, + {"q_credit_vld_22", 4}, + {"q_credit_vld_23", 4}, + {"q_credit_vld_24", 4}, + {"q_credit_vld_25", 4}, + {"q_credit_vld_26", 4}, + {"q_credit_vld_27", 4}, + {"q_credit_vld_28", 4}, + {"q_credit_vld_29", 4}, + {"q_credit_vld_30", 4}, + {"q_credit_vld_31", 4}, + {"gro_bd_serr_cnt", 4}, + {"gro_context_serr_cnt", 4}, + {"rx_stash_cfg_serr_cnt", 4}, + {"rcb_tx_mem_serr_cnt", 4}, + {"gro_bd_merr_cnt", 4}, + {"gro_context_merr_cnt", 4}, + {"rx_stash_cfg_merr_cnt", 4}, + {"rcb_tx_mem_merr_cnt", 4}, + {HNS3_REG_RSV_NAME, 16}, +}; + +const struct hns3_reg_info dfx_tqp_regs[] = { + {"q_num", 4}, + {"rcb_cfg_rx_ring_tail", 4}, + {"rcb_cfg_rx_ring_head", 4}, + {"rcb_cfg_rx_ring_fbdnum", 4}, + {"rcb_cfg_rx_ring_offset", 4}, + {"rcb_cfg_rx_ring_fbdoffset", 4}, + {"rcb_cfg_rx_ring_pktnum_record", 4}, + {"rcb_cfg_tx_ring_tail", 4}, + {"rcb_cfg_tx_ring_head", 4}, + {"rcb_cfg_tx_ring_fbdnum", 4}, + {"rcb_cfg_tx_ring_offset", 4}, + {"rcb_cfg_tx_ring_ebdnum", 4}, +}; + +const struct hns3_reg_info dfx_ssu_2_regs[] = { + {"oq_index", 4}, + {"queue_cnt", 4}, + {HNS3_REG_RSV_NAME, 16}, +}; + +const struct hns3_reg_info vf_cmdq_regs[] = { + {"comm_nic_csq_baseaddr_l", 4}, + {"comm_nic_csq_baseaddr_h", 4}, + {"comm_nic_csq_depth", 4}, + {"comm_nic_csq_tail", 4}, + {"comm_nic_csq_head", 4}, + {"comm_nic_crq_baseaddr_l", 4}, + {"comm_nic_crq_baseaddr_h", 4}, + {"comm_nic_crq_depth", 4}, + {"comm_nic_crq_tail", 4}, + {"comm_nic_crq_head", 4}, + {"comm_vector0_cmdq_src", 4}, + {"comm_vector0_cmdq_state", 4}, + {"comm_cmdq_intr_en", 4}, + {"comm_cmdq_intr_gen", 4}, +}; + +const struct hns3_reg_info vf_common_regs[] = { + {"misc_vector_base", 4}, + {"rst_ing", 4}, + {"gro_en", 4}, +}; + +const struct hns3_reg_info vf_ring_regs[] = { + {"ring_rx_addr_l", 4}, + {"ring_rx_addr_h", 4}, + {"ring_rx_bd_num", 4}, + {"ring_rx_bd_length", 4}, + {"ring_rx_merge_en", 4}, + {"ring_rx_tail", 4}, + {"ring_rx_head", 4}, + {"ring_rx_fbd_num", 4}, + {"ring_rx_offset", 4}, + {"ring_rx_fbd_offset", 4}, + {"ring_rx_stash", 4}, + {"ring_rx_bd_err", 4}, + {"ring_tx_addr_l", 4}, + {"ring_tx_addr_h", 4}, + {"ring_tx_bd_num", 4}, + {"ring_tx_priority", 4}, + {"ring_tx_tc", 4}, + {"ring_tx_merge_en", 4}, + {"ring_tx_tail", 4}, + {"ring_tx_head", 4}, + {"ring_tx_fbd_num", 4}, + {"ring_tx_offset", 4}, + {"ring_tx_ebd_num", 4}, + {"ring_tx_ebd_offset", 4}, + {"ring_tx_bd_err", 4}, + {"ring_en", 4}, +}; + +const struct hns3_reg_info vf_tqp_intr_regs[] = { + {"tqp_intr_ctrl", 4}, + {"tqp_intr_gl0", 4}, + {"tqp_intr_gl1", 4}, + {"tqp_intr_gl2", 4}, + {"tqp_intr_rl", 4}, +}; + +const struct hns3_regs_group pf_regs_groups[] = { + [HNS3_TAG_CMDQ] = {"cmdq_regs", pf_cmdq_regs, ARRAY_SIZE(pf_cmdq_regs)}, + [HNS3_TAG_COMMON] = {"common_regs", pf_common_regs, + ARRAY_SIZE(pf_common_regs)}, + [HNS3_TAG_RING] = {"ring_regs", pf_ring_regs, ARRAY_SIZE(pf_ring_regs)}, + [HNS3_TAG_TQP_INTR] = {"tqp_intr_regs", pf_tqp_intr_regs, + ARRAY_SIZE(pf_tqp_intr_regs)}, + [HNS3_TAG_QUERY_32_BIT] = {"dfx_32_regs", query_32_bit_regs, + ARRAY_SIZE(query_32_bit_regs)}, + [HNS3_TAG_QUERY_64_BIT] = {"dfx_64_regs", query_64_bit_regs, + ARRAY_SIZE(query_64_bit_regs)}, + [HNS3_TAG_DFX_BIOS_COMMON] = {"dfx_bios_common_regs", + dfx_bios_common_regs, + ARRAY_SIZE(dfx_bios_common_regs)}, + [HNS3_TAG_DFX_SSU_0] = {"dfx_ssu_0_regs", dfx_ssu_0_regs, + ARRAY_SIZE(dfx_ssu_0_regs)}, + [HNS3_TAG_DFX_SSU_1] = {"dfx_ssu_1_regs", dfx_ssu_1_regs, + ARRAY_SIZE(dfx_ssu_1_regs)}, + [HNS3_TAG_DFX_IGU_EGU] = {"dfx_igu_egu_regs", dfx_igu_egu_regs, + ARRAY_SIZE(dfx_igu_egu_regs)}, + [HNS3_TAG_DFX_RPU_0] = {"dfx_rpu_0_regs", dfx_rpu_0_regs, + ARRAY_SIZE(dfx_rpu_0_regs)}, + [HNS3_TAG_DFX_RPU_1] = {"dfx_rpu_1_regs", dfx_rpu_1_regs, + ARRAY_SIZE(dfx_rpu_1_regs)}, + [HNS3_TAG_DFX_NCSI] = {"dfx_ncsi_regs", dfx_ncsi_regs, + ARRAY_SIZE(dfx_ncsi_regs)}, + [HNS3_TAG_DFX_RTC] = {"dfx_rtc_regs", dfx_rtc_regs, + ARRAY_SIZE(dfx_rtc_regs)}, + [HNS3_TAG_DFX_PPP] = {"dfx_ppp_regs", dfx_ppp_regs, + ARRAY_SIZE(dfx_ppp_regs)}, + [HNS3_TAG_DFX_RCB] = {"dfx_rcb_regs", dfx_rcb_regs, + ARRAY_SIZE(dfx_rcb_regs)}, + [HNS3_TAG_DFX_TQP] = {"dfx_tqp_regs", dfx_tqp_regs, + ARRAY_SIZE(dfx_tqp_regs)}, + [HNS3_TAG_DFX_SSU_2] = {"dfx_ssu_2_regs", dfx_ssu_2_regs, + ARRAY_SIZE(dfx_ssu_2_regs)}, + [HNS3_TAG_DFX_RPU_TNL] = {"dfx_rpu_tnl", dfx_rpu_0_regs, + ARRAY_SIZE(dfx_rpu_0_regs)}, +}; + +const struct hns3_regs_group vf_regs_groups[] = { + [HNS3_TAG_CMDQ] = {"cmdq_regs", vf_cmdq_regs, ARRAY_SIZE(vf_cmdq_regs)}, + [HNS3_TAG_COMMON] = {"common_regs", vf_common_regs, + ARRAY_SIZE(vf_common_regs)}, + [HNS3_TAG_RING] = {"ring_regs", vf_ring_regs, ARRAY_SIZE(vf_ring_regs)}, + [HNS3_TAG_TQP_INTR] = {"tqp_intr_regs", vf_tqp_intr_regs, + ARRAY_SIZE(vf_tqp_intr_regs)}, +}; + +static void hns3_dump_reg_hex(const char *regs_name, const u8 *regs_data, + u16 value_len, u32 name_max_len) +{ + if (strcmp(regs_name, HNS3_REG_RSV_NAME) == 0) + return; + + fprintf(stdout, " %-*s : ", name_max_len, regs_name); + if (value_len == 4) /* 4 bytes register */ + fprintf(stdout, "0x%08x\n", *(u32 *)regs_data); + if (value_len == 8) /* 8 bytes register */ + fprintf(stdout, "0x%016llx\n", *(u64 *)regs_data); +} + +static u32 hns3_get_group_regs_name_max_len(const struct hns3_regs_group *group) +{ + const struct hns3_reg_info *reg; + u32 max_len = 0; + u16 i; + + for (i = 0; i < group->regs_count; i++) { + reg = &group->regs[i]; + max_len = strlen(reg->name) > max_len ? + strlen(reg->name) : max_len; + } + + return max_len; +} + +static const char *hns3_get_group_name(const struct hns3_regs_group *group, + u32 tag) +{ + static u32 pre_tag = HNS3_TAG_MAX; + static char group_name[256]; + static u32 index; + + if (!hns3_reg_is_repeat_tag_array[tag]) + return group->group_name; + + if (tag != pre_tag) + index = 0; + + pre_tag = tag; + sprintf(group_name, "%s_%u", group->group_name, index++); + return group_name; +} + +static void hns3_dump_reg_group(const struct hns3_regs_group *group, u32 tag, + u32 expected_len, const u8 *regs_data) +{ + u32 name_max_len = hns3_get_group_regs_name_max_len(group); + const struct hns3_reg_info *reg; + u32 dump_offset = 0; + u16 i; + + fprintf(stdout, "[%s]\n", hns3_get_group_name(group, tag)); + for (i = 0; i < group->regs_count && dump_offset < expected_len; i++) { + reg = &group->regs[i]; + hns3_dump_reg_hex(reg->name, regs_data + dump_offset, + reg->value_len, name_max_len); + dump_offset += reg->value_len; + } + + /* the driver may add new register. + * In this case, the register name is unknown. + * The register can be parsed as unknown:value format. + */ + while (dump_offset < expected_len) { + hns3_dump_reg_hex(HNS3_REG_UNKNOW_NAME, regs_data + dump_offset, + HNS3_REG_UNKNOW_VALUE_LEN, name_max_len); + dump_offset += HNS3_REG_UNKNOW_VALUE_LEN; + } +} + +static void hns3_dump_as_groups(const struct hns3_regs_group *groups, + const u8 *regs_data, u32 regs_len) +{ + u32 tlv_size = sizeof(struct hns3_reg_tlv); + const struct hns3_reg_tlv *tlv; + u32 dump_offset = 0; + + while (dump_offset < regs_len) { + tlv = (const struct hns3_reg_tlv *)(regs_data + dump_offset); + hns3_dump_reg_group(&groups[tlv->tag], tlv->tag, + tlv->len - tlv_size, + regs_data + dump_offset + tlv_size); + dump_offset += tlv->len; + } +} + +static bool hns3_dump_validate(const u8 *regs_data, u32 regs_len) +{ + u32 tlv_size = sizeof(struct hns3_reg_tlv); + const struct hns3_reg_tlv *tlv; + u32 dump_offset = 0; + + while (dump_offset < regs_len) { + tlv = (const struct hns3_reg_tlv *)(regs_data + dump_offset); + + /* register value length is 4 bytes or 8 bytes */ + if ((tlv->len - tlv_size) % 4) + return false; + + if (tlv->tag >= HNS3_TAG_MAX) + return false; + + dump_offset += tlv->len; + } + + return dump_offset == regs_len; +} + +int hns3_dump_regs(struct ethtool_drvinfo *info __maybe_unused, + struct ethtool_regs *regs) +{ + const struct hns3_regs_group *groups = pf_regs_groups; + u32 header_len = sizeof(struct hns3_reg_header); + const struct hns3_reg_header *header; + + /* must contain header and register data */ + if (regs->len <= header_len) + return -ENODATA; + + header = (struct hns3_reg_header *)regs->data; + if (header->magic_number != HNS3_REG_MAGIC_NUMBER) + return -EOPNOTSUPP; + + if (!hns3_dump_validate(regs->data + header_len, + regs->len - header_len)) + return -EINVAL; + + if (header->is_vf) + groups = vf_regs_groups; + + hns3_dump_as_groups(groups, regs->data + header_len, + regs->len - header_len); + return 0; +} @@ -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 = @@ -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; @@ -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; +} @@ -21,15 +21,19 @@ #include <unistd.h> #include <endian.h> #include <sys/ioctl.h> -#include <net/if.h> +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#define __UAPI_DEF_IF_IFMAP 1 +#define __UAPI_DEF_IF_IFREQ 1 +#include <linux/if.h> -#define maybe_unused __attribute__((__unused__)) +#include "json_writer.h" +#include "json_print.h" -/* ethtool.h expects these to be defined by <linux/types.h> */ -#ifndef HAVE_BE_TYPES -typedef uint16_t __be16; -typedef uint32_t __be32; -typedef unsigned long long __be64; +#define __maybe_unused __attribute__((__unused__)) + +/* internal for netlink interface */ +#ifdef ETHTOOL_ENABLE_NETLINK +struct nl_context; #endif typedef unsigned long long u64; @@ -38,14 +42,8 @@ typedef uint16_t u16; typedef uint8_t u8; typedef int32_t s32; -/* ethtool.h epxects __KERNEL_DIV_ROUND_UP to be defined by <linux/kernel.h> */ -#include <linux/kernel.h> -#ifndef __KERNEL_DIV_ROUND_UP -#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) -#endif - -#include "ethtool-copy.h" -#include "net_tstamp-copy.h" +#include <linux/ethtool.h> +#include <linux/net_tstamp.h> #if __BYTE_ORDER == __BIG_ENDIAN static inline u16 cpu_to_be16(u16 value) @@ -107,6 +105,20 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr) #define SIOCETHTOOL 0x8946 #endif +/* debugging flags */ +enum { + DEBUG_PARSE, + DEBUG_NL_MSGS, /* incoming/outgoing netlink messages */ + DEBUG_NL_DUMP_SND, /* dump outgoing netlink messages */ + DEBUG_NL_DUMP_RCV, /* dump incoming netlink messages */ + DEBUG_NL_PRETTY_MSG, /* pretty print of messages and errors */ +}; + +static inline bool debug_on(unsigned long debug, unsigned int bit) +{ + return (debug & (1 << bit)); +} + /* Internal values for old-style offload flags. Values and names * must not clash with the flags defined for ETHTOOL_{G,S}FLAGS. */ @@ -190,13 +202,29 @@ static inline int ethtool_link_mode_set_bit(unsigned int nr, u32 *mask) return 0; } +/* Struct for managing module EEPROM pages */ +struct ethtool_module_eeprom { + u32 offset; + u32 length; + u8 page; + u8 bank; + u8 i2c_address; + u8 *data; +}; + /* Context for sub-commands */ 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 }; #ifdef TEST_ETHTOOL @@ -332,6 +360,9 @@ int altera_tse_dump_regs(struct ethtool_drvinfo *info, /* VMware vmxnet3 ethernet controller */ int vmxnet3_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); +/* hns3 ethernet controller */ +int hns3_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + /* Rx flow classification */ int rxclass_parse_ruleopts(struct cmd_context *ctx, struct ethtool_rx_flow_spec *fsp, __u32 *rss_context); @@ -342,13 +373,15 @@ int rxclass_rule_ins(struct cmd_context *ctx, int rxclass_rule_del(struct cmd_context *ctx, __u32 loc); /* Module EEPROM parsing code */ -void sff8079_show_all(const __u8 *id); +void sff8079_show_all_ioctl(const __u8 *id); +int sff8079_show_all_nl(struct cmd_context *ctx); /* Optics diagnostics */ void sff8472_show_all(const __u8 *id); /* QSFP Optics diagnostics */ -void sff8636_show_all(const __u8 *id, __u32 eeprom_len); +void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len); +int sff8636_show_all_nl(struct cmd_context *ctx); /* FUJITSU Extended Socket network device */ int fjes_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); @@ -362,4 +395,19 @@ 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); +/* Freescale/NXP ENETC Ethernet Controller */ +int fsl_enetc_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); + +/* TI CPSW Ethernet Switch */ +int cpsw_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + +/* Microchip Ethernet Controller */ +int lan743x_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + #endif /* ETHTOOL_INTERNAL_H__ */ @@ -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; @@ -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; @@ -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..4f62767 --- /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 array delimiter in non-json context. + */ +void open_json_array(const char *key, const char *str) +{ + if (is_json_context()) { + if (key) + jsonw_name(_jw, key); + jsonw_start_array(_jw); + } else { + printf("%s", str); + } +} + +/* + * End json array or string array + */ +void close_json_array(const char *delim) +{ + if (is_json_context()) + jsonw_end_array(_jw); + else + printf("%s", delim); +} + +/* + * 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..df15314 --- /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(const char *key, const char *str); +void close_json_array(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/lan743x.c b/lan743x.c new file mode 100644 index 0000000..f430ee8 --- /dev/null +++ b/lan743x.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. */ + +#include <stdio.h> +#include <string.h> +#include "internal.h" + +#define LAN743X_ETH_REG_VERSION 1 + +enum { + ETH_PRIV_FLAGS, + ETH_ID_REV, + ETH_FPGA_REV, + ETH_STRAP_READ, + ETH_INT_STS, + ETH_HW_CFG, + ETH_PMT_CTL, + ETH_E2P_CMD, + ETH_E2P_DATA, + ETH_MAC_CR, + ETH_MAC_RX, + ETH_MAC_TX, + ETH_FLOW, + ETH_MII_ACC, + ETH_MII_DATA, + ETH_EEE_TX_LPI_REQ_DLY, + ETH_WUCSR, + ETH_WK_SRC, + + /* Add new registers above */ + MAX_LAN743X_ETH_REGS +}; + +void lan743x_comm_dump_regs(struct ethtool_drvinfo *info __maybe_unused, + struct ethtool_regs *regs) +{ + u32 *lan743x_reg = (u32 *)regs->data; + + fprintf(stdout, "LAN743x Registers:\n"); + fprintf(stdout, "------------------\n"); + fprintf(stdout, "CHIP_ID_REV = 0x%08X\n", lan743x_reg[ETH_ID_REV]); + fprintf(stdout, "FPGA_REV = 0x%08X\n", lan743x_reg[ETH_FPGA_REV]); + fprintf(stdout, "STRAP_READ = 0x%08X\n", lan743x_reg[ETH_STRAP_READ]); + fprintf(stdout, "INT_STS = 0x%08X\n", lan743x_reg[ETH_INT_STS]); + fprintf(stdout, "HW_CFG = 0x%08X\n", lan743x_reg[ETH_HW_CFG]); + fprintf(stdout, "PMT_CTRL = 0x%08X\n", lan743x_reg[ETH_PMT_CTL]); + fprintf(stdout, "E2P_CMD = 0x%08X\n", lan743x_reg[ETH_E2P_CMD]); + fprintf(stdout, "E2P_DATA = 0x%08X\n", lan743x_reg[ETH_E2P_DATA]); + fprintf(stdout, "\n"); + + fprintf(stdout, "MAC Registers:\n"); + fprintf(stdout, "--------------\n"); + fprintf(stdout, "MAC_CR = 0x%08X\n", lan743x_reg[ETH_MAC_CR]); + fprintf(stdout, "MAC_RX = 0x%08X\n", lan743x_reg[ETH_MAC_RX]); + fprintf(stdout, "MAC_TX = 0x%08X\n", lan743x_reg[ETH_MAC_TX]); + fprintf(stdout, "FLOW = 0x%08X\n", lan743x_reg[ETH_FLOW]); + fprintf(stdout, "MII_ACC = 0x%08X\n", lan743x_reg[ETH_MII_ACC]); + fprintf(stdout, "MII_DATA = 0x%08X\n", lan743x_reg[ETH_MII_DATA]); + fprintf(stdout, "WUCSR = 0x%08X\n", lan743x_reg[ETH_WUCSR]); + fprintf(stdout, "WK_SRC = 0x%08X\n", lan743x_reg[ETH_WK_SRC]); + fprintf(stdout, "EEE_TX_LPI_REQ_DLY = 0x%08X\n", + lan743x_reg[ETH_EEE_TX_LPI_REQ_DLY]); + fprintf(stdout, "\n"); +} + +int lan743x_dump_regs(struct ethtool_drvinfo *info __maybe_unused, + struct ethtool_regs *regs) +{ + + lan743x_comm_dump_regs(info, regs); + + return 0; +} @@ -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/libmnl/.gitignore b/libmnl/.gitignore new file mode 100644 index 0000000..b6b8d60 --- /dev/null +++ b/libmnl/.gitignore @@ -0,0 +1,20 @@ +*~ +*.la +*.lo +*.o +.deps/ +.libs/ +Makefile +Makefile.in + +/aclocal.m4 +/autom4te.cache/ +/build-aux/ +/config.* +/configure +/libtool +/stamp-h1 + +/libmnl.pc + +/libmnl-*.tar.bz2 diff --git a/libmnl/COPYING b/libmnl/COPYING new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/libmnl/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libmnl/Make_global.am b/libmnl/Make_global.am new file mode 100644 index 0000000..7810dab --- /dev/null +++ b/libmnl/Make_global.am @@ -0,0 +1,24 @@ +# This is _NOT_ the library release version, it's an API version. +# Extracted from Chapter 6 "Library interface versions" of the libtool docs. +# +# <snippet> +# Here are a set of rules to help you update your library version information: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# 2. Update the version information only immediately before a public release +# of your software. More frequent updates are unnecessary, and only guarantee +# that the current interface number gets larger faster. +# 3. If the library source code has changed at all since the last update, +# then increment revision (`c:r:a' becomes `c:r+1:a'). +# 4. If any interfaces have been added, removed, or changed since the last +# update, increment current, and set revision to 0. +# 5. If any interfaces have been added since the last public release, then +# increment age. +# 6. If any interfaces have been removed since the last public release, then +# set age to 0. +# </snippet> +# +LIBVERSION=2:0:2 + +AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include +AM_CFLAGS = ${regular_CFLAGS} ${GCC_FVISIBILITY_HIDDEN} diff --git a/libmnl/Makefile.am b/libmnl/Makefile.am new file mode 100644 index 0000000..94e6935 --- /dev/null +++ b/libmnl/Makefile.am @@ -0,0 +1,11 @@ +include $(top_srcdir)/Make_global.am + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src include examples doxygen +DIST_SUBDIRS = src include examples doxygen + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libmnl.pc + +${pkgconfig_DATA}: ${top_builddir}/config.status diff --git a/libmnl/README b/libmnl/README new file mode 100644 index 0000000..fbac9d2 --- /dev/null +++ b/libmnl/README @@ -0,0 +1,28 @@ += What is libmnl? = + +libmnl is a minimalistic user-space library oriented to Netlink developers. +There are a lot of common tasks in parsing, validating, constructing of +both the Netlink header and TLVs that are repetitive and easy to get wrong. +This library aims to provide simple helpers that allows you to re-use code +and to avoid re-inventing the wheel. The main features of this library are: + +* Small: the shared library requires around 30KB for an x86-based computer. +* Simple: this library avoids complexity and elaborated abstractions that +tend to hide Netlink details. +* Easy to use: the library simplifies the work for Netlink-wise developers. +It provides functions to make socket handling, message building, validating, +parsing and sequence tracking, easier. +* Easy to re-use: you can use the library to build your own abstraction layer +on top of this library. +* Decoupling: the interdependency of the main bricks that compose the library +is reduced, i.e. the library provides many helpers, but the programmer is not +forced to use them. + += Example files = + +You can find several example files under examples/ that you can compile by +invoking `make check'. + +-- +08/sep/2010 +Pablo Neira Ayuso <pablo@netfilter.org> diff --git a/libmnl/autogen.sh b/libmnl/autogen.sh new file mode 100755 index 0000000..5e1344a --- /dev/null +++ b/libmnl/autogen.sh @@ -0,0 +1,4 @@ +#!/bin/sh -e + +autoreconf -fi +rm -Rf autom4te.cache diff --git a/libmnl/config.h b/libmnl/config.h new file mode 100644 index 0000000..f7c60e2 --- /dev/null +++ b/libmnl/config.h @@ -0,0 +1 @@ +#define HAVE_VISIBILITY_HIDDEN 1 diff --git a/libmnl/configure.ac b/libmnl/configure.ac new file mode 100644 index 0000000..4698aec --- /dev/null +++ b/libmnl/configure.ac @@ -0,0 +1,70 @@ +dnl Process this file with autoconf to create configure. + +AC_INIT([libmnl], [1.0.5]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CANONICAL_HOST +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([foreign tar-pax no-dist-gzip dist-xz 1.6 subdir-objects]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_PROG_CC +AM_PROG_CC_C_O +AC_EXEEXT +AC_DISABLE_STATIC +LT_INIT +CHECK_GCC_FVISIBILITY +case "$host" in +*-*-linux* | *-*-uclinux*) ;; +*) AC_MSG_ERROR([Linux only, dude!]);; +esac + +regular_CPPFLAGS="-D_FILE_OFFSET_BITS=64 -D_REENTRANT" +regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \ + -Wmissing-prototypes -Wshadow -Wstrict-prototypes \ + -Wformat=2 -pipe" +AC_SUBST([regular_CPPFLAGS]) +AC_SUBST([regular_CFLAGS]) +AC_CONFIG_FILES([Makefile + src/Makefile + include/Makefile + include/libmnl/Makefile + include/linux/Makefile + include/linux/can/Makefile + include/linux/netfilter/Makefile + examples/Makefile + examples/genl/Makefile + examples/kobject/Makefile + examples/netfilter/Makefile + examples/rtnl/Makefile + libmnl.pc + doxygen/doxygen.cfg + doxygen/Makefile]) + +AC_ARG_WITH([doxygen], [AS_HELP_STRING([--with-doxygen], + [create doxygen documentation])], + [with_doxygen="$withval"], [with_doxygen=yes]) + +AS_IF([test "x$with_doxygen" != xno], [ + AC_CHECK_PROGS([DOXYGEN], [doxygen]) + AC_CHECK_PROGS([DOT], [dot], [""]) + AS_IF([test "x$DOT" != "x"], + [AC_SUBST(HAVE_DOT, YES)], + [AC_SUBST(HAVE_DOT, NO)]) +]) + +AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) +AS_IF([test "x$DOXYGEN" = x], [ + AS_IF([test "x$with_doxygen" != xno], [ + dnl Only run doxygen Makefile if doxygen installed + AC_MSG_WARN([Doxygen not found - continuing without Doxygen support]) + with_doxygen=no + ]) +]) +AC_OUTPUT + +echo " +libmnl configuration: + doxygen: ${with_doxygen}" diff --git a/libmnl/doxygen/.gitignore b/libmnl/doxygen/.gitignore new file mode 100644 index 0000000..2196cf8 --- /dev/null +++ b/libmnl/doxygen/.gitignore @@ -0,0 +1,4 @@ +doxyfile.stamp +doxygen.cfg +html/ +man/ diff --git a/libmnl/doxygen/Makefile.am b/libmnl/doxygen/Makefile.am new file mode 100644 index 0000000..4770fc7 --- /dev/null +++ b/libmnl/doxygen/Makefile.am @@ -0,0 +1,25 @@ +if HAVE_DOXYGEN +doc_srcs = $(shell find $(top_srcdir)/src -name '*.c') + +doxyfile.stamp: $(doc_srcs) Makefile.am + rm -rf html man + doxygen doxygen.cfg >/dev/null + $(SHELL) $(top_srcdir)/doxygen/finalize_manpages.sh + touch doxyfile.stamp + +CLEANFILES = doxyfile.stamp + +all-local: doxyfile.stamp +clean-local: + rm -rf $(top_srcdir)/doxygen/man $(top_srcdir)/doxygen/html +install-data-local: + mkdir -p $(DESTDIR)$(mandir)/man3 + cp --no-dereference --preserve=links,mode,timestamps man/man3/*.3\ + $(DESTDIR)$(mandir)/man3/ + +# make distcheck needs uninstall-local +uninstall-local: + rm -r $(DESTDIR)$(mandir) man html doxyfile.stamp +endif + +EXTRA_DIST = finalize_manpages.sh diff --git a/libmnl/doxygen/doxygen.cfg.in b/libmnl/doxygen/doxygen.cfg.in new file mode 100644 index 0000000..24089ac --- /dev/null +++ b/libmnl/doxygen/doxygen.cfg.in @@ -0,0 +1,23 @@ +# Difference with default Doxyfile 1.8.20 +PROJECT_NAME = @PACKAGE@ +PROJECT_NUMBER = @VERSION@ +OUTPUT_DIRECTORY = . +ABBREVIATE_BRIEF = +FULL_PATH_NAMES = NO +TAB_SIZE = 8 +OPTIMIZE_OUTPUT_FOR_C = YES +INPUT = @top_srcdir@ +FILE_PATTERNS = */src/*.c +RECURSIVE = YES +EXCLUDE_SYMBOLS = EXPORT_SYMBOL mnl_nlmsg_batch mnl_socket +EXAMPLE_PATTERNS = +INPUT_FILTER = "sed 's/EXPORT_SYMBOL//g'" +SOURCE_BROWSER = YES +ALPHABETICAL_INDEX = NO +SEARCHENGINE = NO +GENERATE_LATEX = NO +LATEX_CMD_NAME = latex +GENERATE_MAN = YES +MAN_LINKS = YES +HAVE_DOT = @HAVE_DOT@ +DOT_TRANSPARENT = YES diff --git a/libmnl/doxygen/finalize_manpages.sh b/libmnl/doxygen/finalize_manpages.sh new file mode 100644 index 0000000..6f230b1 --- /dev/null +++ b/libmnl/doxygen/finalize_manpages.sh @@ -0,0 +1,40 @@ +# +# We need to use bash for its associative array facility +# +[ "$BASH" ] || exec bash $0 +# +# (`bash -p` prevents import of functions from the environment). +# +set -p + +declare -A renamed_page + +main(){ set -e; cd man/man3; rm -f _* + count_real_pages + rename_real_pages + make_symlinks +} + +count_real_pages(){ page_count=0 + for i in $(ls -S) + do head -n1 $i | grep -E -q '^\.so' && break + page_count=$(($page_count + 1)) + done + first_link=$(($page_count + 1)) +} + +rename_real_pages(){ for i in $(ls -S | head -n$page_count) + do for j in $(ls -S | tail -n+$first_link) + do grep -E -q $i$ $j && break + done + mv -f $i $j + renamed_page[$i]=$j + done +} + +make_symlinks(){ for j in $(ls -S | tail -n+$first_link) + do ln -sf ${renamed_page[$(cat $j | cut -f2 -d/)]} $j + done +} + +main diff --git a/libmnl/examples/Makefile.am b/libmnl/examples/Makefile.am new file mode 100644 index 0000000..e5cb052 --- /dev/null +++ b/libmnl/examples/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = genl kobject netfilter rtnl diff --git a/libmnl/examples/genl/.gitignore b/libmnl/examples/genl/.gitignore new file mode 100644 index 0000000..a7d8966 --- /dev/null +++ b/libmnl/examples/genl/.gitignore @@ -0,0 +1,2 @@ +/genl-family-get +/genl-group-events
\ No newline at end of file diff --git a/libmnl/examples/genl/Makefile.am b/libmnl/examples/genl/Makefile.am new file mode 100644 index 0000000..b4b7954 --- /dev/null +++ b/libmnl/examples/genl/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/Make_global.am + +check_PROGRAMS = genl-family-get \ + genl-group-events + +genl_family_get_SOURCES = genl-family-get.c +genl_family_get_LDADD = ../../src/libmnl.la + +genl_group_events_SOURCES = genl-group-events.c +genl_group_events_LDADD = ../../src/libmnl.la diff --git a/libmnl/examples/genl/genl-family-get.c b/libmnl/examples/genl/genl-family-get.c new file mode 100644 index 0000000..ba8de12 --- /dev/null +++ b/libmnl/examples/genl/genl-family-get.c @@ -0,0 +1,241 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> + +#include <libmnl/libmnl.h> +#include <linux/genetlink.h> + +static int parse_mc_grps_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTRL_ATTR_MCAST_GRP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTRL_ATTR_MCAST_GRP_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_genl_mc_grps(struct nlattr *nested) +{ + struct nlattr *pos; + + mnl_attr_for_each_nested(pos, nested) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1] = {}; + + mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb); + if (tb[CTRL_ATTR_MCAST_GRP_ID]) { + printf("id-0x%x ", + mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID])); + } + if (tb[CTRL_ATTR_MCAST_GRP_NAME]) { + printf("name: %s ", + mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME])); + } + printf("\n"); + } +} + +static int parse_family_ops_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_OP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTRL_ATTR_OP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTRL_ATTR_OP_MAX: + break; + default: + return MNL_CB_OK; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_genl_family_ops(struct nlattr *nested) +{ + struct nlattr *pos; + + mnl_attr_for_each_nested(pos, nested) { + struct nlattr *tb[CTRL_ATTR_OP_MAX+1] = {}; + + mnl_attr_parse_nested(pos, parse_family_ops_cb, tb); + if (tb[CTRL_ATTR_OP_ID]) { + printf("id-0x%x ", + mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID])); + } + if (tb[CTRL_ATTR_OP_MAX]) { + printf("flags "); + } + printf("\n"); + } +} + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTRL_ATTR_FAMILY_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTRL_ATTR_FAMILY_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTRL_ATTR_VERSION: + case CTRL_ATTR_HDRSIZE: + case CTRL_ATTR_MAXATTR: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTRL_ATTR_OPS: + case CTRL_ATTR_MCAST_GROUPS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTRL_ATTR_MAX+1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), data_attr_cb, tb); + if (tb[CTRL_ATTR_FAMILY_NAME]) { + printf("name=%s\t", + mnl_attr_get_str(tb[CTRL_ATTR_FAMILY_NAME])); + } + if (tb[CTRL_ATTR_FAMILY_ID]) { + printf("id=%u\t", + mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID])); + } + if (tb[CTRL_ATTR_VERSION]) { + printf("version=%u\t", + mnl_attr_get_u32(tb[CTRL_ATTR_VERSION])); + } + if (tb[CTRL_ATTR_HDRSIZE]) { + printf("hdrsize=%u\t", + mnl_attr_get_u32(tb[CTRL_ATTR_HDRSIZE])); + } + if (tb[CTRL_ATTR_MAXATTR]) { + printf("maxattr=%u\t", + mnl_attr_get_u32(tb[CTRL_ATTR_MAXATTR])); + } + printf("\n"); + if (tb[CTRL_ATTR_OPS]) { + printf("ops:\n"); + parse_genl_family_ops(tb[CTRL_ATTR_OPS]); + } + if (tb[CTRL_ATTR_MCAST_GROUPS]) { + printf("grps:\n"); + parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS]); + } + printf("\n"); + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + int ret; + unsigned int seq, portid; + + if (argc > 2) { + printf("%s [family name]\n", argv[0]); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = GENL_ID_CTRL; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd = CTRL_CMD_GETFAMILY; + genl->version = 1; + + mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL); + if (argc >= 2) + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, argv[1]); + else + nlh->nlmsg_flags |= NLM_F_DUMP; + + nl = mnl_socket_open(NETLINK_GENERIC); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/genl/genl-group-events.c b/libmnl/examples/genl/genl-group-events.c new file mode 100644 index 0000000..d5f0a18 --- /dev/null +++ b/libmnl/examples/genl/genl-group-events.c @@ -0,0 +1,63 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> + +#include <libmnl/libmnl.h> +#include <linux/genetlink.h> + +static int group; + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + printf("received event type=%d from genetlink group %d\n", + nlh->nlmsg_type, group); + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + if (argc != 2) { + printf("%s [group]\n", argv[0]); + exit(EXIT_FAILURE); + } + group = atoi(argv[1]); + + nl = mnl_socket_open(NETLINK_GENERIC); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_setsockopt(nl, NETLINK_ADD_MEMBERSHIP, &group, + sizeof(int)) < 0) { + perror("mnl_socket_setsockopt"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/kobject/.gitignore b/libmnl/examples/kobject/.gitignore new file mode 100644 index 0000000..4d95e59 --- /dev/null +++ b/libmnl/examples/kobject/.gitignore @@ -0,0 +1 @@ +/kobject-event diff --git a/libmnl/examples/kobject/Makefile.am b/libmnl/examples/kobject/Makefile.am new file mode 100644 index 0000000..9197f7a --- /dev/null +++ b/libmnl/examples/kobject/Makefile.am @@ -0,0 +1,6 @@ +include $(top_srcdir)/Make_global.am + +check_PROGRAMS = kobject-event + +kobject_event_SOURCES = kobject-event.c +kobject_event_LDADD = ../../src/libmnl.la diff --git a/libmnl/examples/kobject/kobject-event.c b/libmnl/examples/kobject/kobject-event.c new file mode 100644 index 0000000..97debdf --- /dev/null +++ b/libmnl/examples/kobject/kobject-event.c @@ -0,0 +1,49 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> + +#include <libmnl/libmnl.h> +#include <linux/netlink.h> + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + nl = mnl_socket_open(NETLINK_KOBJECT_UEVENT); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + /* There is one single group in kobject over netlink */ + if (mnl_socket_bind(nl, (1<<0), MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + int i; + + /* kobject uses a string based protocol, with no initial + * netlink header. + */ + for (i=0; i<ret; i++) + printf("%c", buf[i]); + + printf("\n"); + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/netfilter/.gitignore b/libmnl/examples/netfilter/.gitignore new file mode 100644 index 0000000..bbeb031 --- /dev/null +++ b/libmnl/examples/netfilter/.gitignore @@ -0,0 +1,6 @@ +/nf-log +/nf-queue +/nfct-create-batch +/nfct-daemon +/nfct-dump +/nfct-event diff --git a/libmnl/examples/netfilter/Makefile.am b/libmnl/examples/netfilter/Makefile.am new file mode 100644 index 0000000..4bae05f --- /dev/null +++ b/libmnl/examples/netfilter/Makefile.am @@ -0,0 +1,26 @@ +include $(top_srcdir)/Make_global.am + +check_PROGRAMS = nf-queue \ + nf-log \ + nfct-dump \ + nfct-event \ + nfct-create-batch \ + nfct-daemon + +nf_queue_SOURCES = nf-queue.c +nf_queue_LDADD = ../../src/libmnl.la + +nf_log_SOURCES = nf-log.c +nf_log_LDADD = ../../src/libmnl.la + +nfct_dump_SOURCES = nfct-dump.c +nfct_dump_LDADD = ../../src/libmnl.la + +nfct_daemon_SOURCES = nfct-daemon.c +nfct_daemon_LDADD = ../../src/libmnl.la + +nfct_event_SOURCES = nfct-event.c +nfct_event_LDADD = ../../src/libmnl.la + +nfct_create_batch_SOURCES = nfct-create-batch.c +nfct_create_batch_LDADD = ../../src/libmnl.la diff --git a/libmnl/examples/netfilter/nf-log.c b/libmnl/examples/netfilter/nf-log.c new file mode 100644 index 0000000..4383b66 --- /dev/null +++ b/libmnl/examples/netfilter/nf-log.c @@ -0,0 +1,219 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_log.h> + +static int parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, NFULA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFULA_MARK: + case NFULA_IFINDEX_INDEV: + case NFULA_IFINDEX_OUTDEV: + case NFULA_IFINDEX_PHYSINDEV: + case NFULA_IFINDEX_PHYSOUTDEV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFULA_TIMESTAMP: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfulnl_msg_packet_timestamp)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFULA_HWADDR: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfulnl_msg_packet_hw)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFULA_PREFIX: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFULA_PAYLOAD: + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int log_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[NFULA_MAX+1] = {}; + struct nfulnl_msg_packet_hdr *ph = NULL; + const char *prefix = NULL; + uint32_t mark = 0; + + mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb); + if (tb[NFULA_PACKET_HDR]) + ph = mnl_attr_get_payload(tb[NFULA_PACKET_HDR]); + if (tb[NFULA_PREFIX]) + prefix = mnl_attr_get_str(tb[NFULA_PREFIX]); + if (tb[NFULA_MARK]) + mark = ntohl(mnl_attr_get_u32(tb[NFULA_MARK])); + + printf("log received (prefix=\"%s\" hw=0x%04x hook=%u mark=%u)\n", + prefix ? prefix : "", ntohs(ph->hw_protocol), ph->hook, + mark); + + return MNL_CB_OK; +} + +static struct nlmsghdr * +nflog_build_cfg_pf_request(char *buf, uint8_t command) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + + struct nfulnl_msg_config_cmd cmd = { + .command = command, + }; + mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd); + + return nlh; +} + +static struct nlmsghdr * +nflog_build_cfg_request(char *buf, uint8_t command, int qnum) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(qnum); + + struct nfulnl_msg_config_cmd cmd = { + .command = command, + }; + mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd); + + return nlh; +} + +static struct nlmsghdr * +nflog_build_cfg_params(char *buf, uint8_t mode, int range, int qnum) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(qnum); + + struct nfulnl_msg_config_mode params = { + .copy_range = htonl(range), + .copy_mode = mode, + }; + mnl_attr_put(nlh, NFULA_CFG_MODE, sizeof(params), ¶ms); + + return nlh; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int ret; + unsigned int portid, qnum; + + if (argc != 2) { + printf("Usage: %s [queue_num]\n", argv[0]); + exit(EXIT_FAILURE); + } + qnum = atoi(argv[1]); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_UNBIND); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_BIND); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nflog_build_cfg_request(buf, NFULNL_CFG_CMD_BIND, qnum); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nflog_build_cfg_params(buf, NFULNL_COPY_PACKET, 0xFFFF, qnum); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + while (ret > 0) { + ret = mnl_cb_run(buf, ret, 0, portid, log_cb, NULL); + if (ret < 0){ + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/netfilter/nf-queue.c b/libmnl/examples/netfilter/nf-queue.c new file mode 100644 index 0000000..957e365 --- /dev/null +++ b/libmnl/examples/netfilter/nf-queue.c @@ -0,0 +1,243 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_queue.h> + +static int parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, NFQA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFQA_MARK: + case NFQA_IFINDEX_INDEV: + case NFQA_IFINDEX_OUTDEV: + case NFQA_IFINDEX_PHYSINDEV: + case NFQA_IFINDEX_PHYSOUTDEV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFQA_TIMESTAMP: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfqnl_msg_packet_timestamp)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFQA_HWADDR: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfqnl_msg_packet_hw)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFQA_PAYLOAD: + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int queue_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[NFQA_MAX+1] = {}; + struct nfqnl_msg_packet_hdr *ph = NULL; + uint32_t id = 0; + + mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb); + if (tb[NFQA_PACKET_HDR]) { + ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]); + id = ntohl(ph->packet_id); + + printf("packet received (id=%u hw=0x%04x hook=%u)\n", + id, ntohs(ph->hw_protocol), ph->hook); + } + + return MNL_CB_OK + id; +} + +static struct nlmsghdr * +nfq_build_cfg_pf_request(char *buf, uint8_t command) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + + struct nfqnl_msg_config_cmd cmd = { + .command = command, + .pf = htons(AF_INET), + }; + mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd); + + return nlh; +} + +static struct nlmsghdr * +nfq_build_cfg_request(char *buf, uint8_t command, int queue_num) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + struct nfqnl_msg_config_cmd cmd = { + .command = command, + .pf = htons(AF_INET), + }; + mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd); + + return nlh; +} + +static struct nlmsghdr * +nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + struct nfqnl_msg_config_params params = { + .copy_range = htonl(range), + .copy_mode = mode, + }; + mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), ¶ms); + + return nlh; +} + +static struct nlmsghdr * +nfq_build_verdict(char *buf, int id, int queue_num, int verd) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT; + nlh->nlmsg_flags = NLM_F_REQUEST; + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + struct nfqnl_msg_verdict_hdr vh = { + .verdict = htonl(verd), + .id = htonl(id), + }; + mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh); + + return nlh; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int ret; + unsigned int portid, queue_num; + + if (argc != 2) { + printf("Usage: %s [queue_num]\n", argv[0]); + exit(EXIT_FAILURE); + } + queue_num = atoi(argv[1]); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_UNBIND); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_BIND); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + while (ret > 0) { + uint32_t id; + + ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL); + if (ret < 0){ + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + id = ret - MNL_CB_OK; + nlh = nfq_build_verdict(buf, id, queue_num, NF_ACCEPT); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/netfilter/nfct-create-batch.c b/libmnl/examples/netfilter/nfct-create-batch.c new file mode 100644 index 0000000..4675789 --- /dev/null +++ b/libmnl/examples/netfilter/nfct-create-batch.c @@ -0,0 +1,187 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <time.h> +#include <sys/select.h> +#include <string.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_conntrack.h> +#include <linux/netfilter/nf_conntrack_common.h> +#include <linux/netfilter/nf_conntrack_tcp.h> + +static void put_msg(char *buf, uint16_t i, int seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + struct nlattr *nest1, *nest2; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; + nlh->nlmsg_seq = seq; + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = AF_INET; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_ORIG); + nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP); + mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("1.1.1.1")); + mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("2.2.2.2")); + mnl_attr_nest_end(nlh, nest2); + + nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO); + mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP); + mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(i)); + mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(1025)); + mnl_attr_nest_end(nlh, nest2); + mnl_attr_nest_end(nlh, nest1); + + nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_REPLY); + nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP); + mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("2.2.2.2")); + mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("1.1.1.1")); + mnl_attr_nest_end(nlh, nest2); + + nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO); + mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP); + mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(1025)); + mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(i)); + mnl_attr_nest_end(nlh, nest2); + mnl_attr_nest_end(nlh, nest1); + + nest1 = mnl_attr_nest_start(nlh, CTA_PROTOINFO); + nest2 = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP); + mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE, TCP_CONNTRACK_SYN_SENT); + mnl_attr_nest_end(nlh, nest2); + mnl_attr_nest_end(nlh, nest1); + + mnl_attr_put_u32(nlh, CTA_STATUS, htonl(IPS_CONFIRMED)); + mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(1000)); +} + +static int cb_err(const struct nlmsghdr *nlh, void *data) +{ + struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + if (err->error != 0) + printf("message with seq %u has failed: %s\n", + nlh->nlmsg_seq, strerror(-err->error)); + return MNL_CB_OK; +} + +static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = { + [NLMSG_ERROR] = cb_err, +}; + +static void +send_batch(struct mnl_socket *nl, struct mnl_nlmsg_batch *b, int portid) +{ + int ret, fd = mnl_socket_get_fd(nl); + size_t len = mnl_nlmsg_batch_size(b); + char rcv_buf[MNL_SOCKET_BUFFER_SIZE]; + + ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b), len); + if (ret == -1) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + /* receive and digest all the acknowledgments from the kernel. */ + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 0 + }; + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + ret = select(fd+1, &readfds, NULL, NULL, &tv); + if (ret == -1) { + perror("select"); + exit(EXIT_FAILURE); + } + while (ret > 0 && FD_ISSET(fd, &readfds)) { + ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run2(rcv_buf, ret, 0, portid, + NULL, NULL, cb_ctl_array, + MNL_ARRAY_SIZE(cb_ctl_array)); + if (ret == -1) { + perror("mnl_cb_run2"); + exit(EXIT_FAILURE); + } + + ret = select(fd+1, &readfds, NULL, NULL, &tv); + if (ret == -1) { + perror("select"); + exit(EXIT_FAILURE); + } + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + } +} + +int main(void) +{ + struct mnl_socket *nl; + char snd_buf[MNL_SOCKET_BUFFER_SIZE*2]; + struct mnl_nlmsg_batch *b; + int j; + unsigned int seq, portid; + uint16_t i; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + /* The buffer that we use to batch messages is MNL_SOCKET_BUFFER_SIZE + * multiplied by 2 bytes long, but we limit the batch to half of it + * since the last message that does not fit the batch goes over the + * upper boundary, if you break this rule, expect memory corruptions. */ + b = mnl_nlmsg_batch_start(snd_buf, MNL_SOCKET_BUFFER_SIZE); + if (b == NULL) { + perror("mnl_nlmsg_batch_start"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + for (i=1024, j=0; i<65535; i++, j++) { + put_msg(mnl_nlmsg_batch_current(b), i, seq+j); + + /* is there room for more messages in this batch? + * if so, continue. */ + if (mnl_nlmsg_batch_next(b)) + continue; + + send_batch(nl, b, portid); + + /* this moves the last message that did not fit into the + * batch to the head of it. */ + mnl_nlmsg_batch_reset(b); + } + + /* check if there is any message in the batch not sent yet. */ + if (!mnl_nlmsg_batch_is_empty(b)) + send_batch(nl, b, portid); + + mnl_nlmsg_batch_stop(b); + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/netfilter/nfct-daemon.c b/libmnl/examples/netfilter/nfct-daemon.c new file mode 100644 index 0000000..d223ac2 --- /dev/null +++ b/libmnl/examples/netfilter/nfct-daemon.c @@ -0,0 +1,365 @@ +/* A very simple skeleton code that implements a daemon that collects + * conntrack statistics from ctnetlink. + * + * This example is placed in the public domain. + */ +#include <endian.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/select.h> + +#include <libmnl/libmnl.h> +#include <linux/netlink.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_conntrack.h> + +#include <sys/queue.h> + +struct nstats { + LIST_ENTRY(nstats) list; + + uint8_t family; + + union { + struct in_addr ip; + struct in6_addr ip6; + }; + uint64_t pkts, bytes; +}; + +static LIST_HEAD(nstats_head, nstats) nstats_head; + +static int parse_counters_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_COUNTERS_PACKETS: + case CTA_COUNTERS_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_counters(const struct nlattr *nest, struct nstats *ns) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_counters_cb, tb); + if (tb[CTA_COUNTERS_PACKETS]) + ns->pkts += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])); + + if (tb[CTA_COUNTERS_BYTES]) + ns->bytes += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES])); +} + +static int parse_ip_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_IP_V4_SRC: + case CTA_IP_V4_DST: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_IP_V6_SRC: + case CTA_IP_V6_DST: + if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, + sizeof(struct in6_addr)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_ip(const struct nlattr *nest, struct nstats *ns) +{ + struct nlattr *tb[CTA_IP_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_ip_cb, tb); + if (tb[CTA_IP_V4_SRC]) { + struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]); + ns->ip = *in; + ns->family = AF_INET; + } + if (tb[CTA_IP_V6_SRC]) { + struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]); + ns->ip6 = *in; + ns->family = AF_INET6; + } +} + +static int parse_tuple_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_IP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_tuple(const struct nlattr *nest, struct nstats *ns) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_tuple_cb, tb); + if (tb[CTA_TUPLE_IP]) + parse_ip(tb[CTA_TUPLE_IP], ns); +} + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_ORIG: + case CTA_COUNTERS_ORIG: + case CTA_COUNTERS_REPLY: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTA_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + struct nstats ns = {}, *cur, *new; + + mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb); + if (tb[CTA_TUPLE_ORIG]) + parse_tuple(tb[CTA_TUPLE_ORIG], &ns); + + if (tb[CTA_COUNTERS_ORIG]) + parse_counters(tb[CTA_COUNTERS_ORIG], &ns); + + if (tb[CTA_COUNTERS_REPLY]) + parse_counters(tb[CTA_COUNTERS_REPLY], &ns); + + /* Look up for existing statistics object ... */ + LIST_FOREACH(cur, &nstats_head, list) { + if (memcmp(&ns.ip6, &cur->ip6, sizeof(struct in6_addr)) == 0) { + /* ... and sum counters */ + cur->pkts += ns.pkts; + cur->bytes += ns.bytes; + return MNL_CB_OK; + } + } + + /* ... if it does not exist, add new stats object */ + new = calloc(1, sizeof(struct nstats)); + if (!new) + return MNL_CB_OK; + + new->family = ns.family; + new->ip6 = ns.ip6; + new->pkts = ns.pkts; + new->bytes = ns.bytes; + + LIST_INSERT_HEAD(&nstats_head, new, list); + + return MNL_CB_OK; +} + +static int handle(struct mnl_socket *nl) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + /* It only happens if NETLINK_NO_ENOBUFS is not set, it means + * we are leaking statistics. + */ + if (errno == ENOBUFS) { + fprintf(stderr, "The daemon has hit ENOBUFS, you can " + "increase the size of your receiver " + "buffer to mitigate this or enable " + "reliable delivery.\n"); + } else { + perror("mnl_socket_recvfrom"); + } + return -1; + } + + ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return -1; + } else if (ret <= MNL_CB_STOP) + return 0; + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + struct nstats *cur; + struct timeval tv = {}; + int ret, secs, on = 1, buffersize = (1 << 22); + + if (argc != 2) { + printf("Usage: %s <poll-secs>\n", argv[0]); + exit(EXIT_FAILURE); + } + secs = atoi(argv[1]); + + LIST_INIT(&nstats_head); + + printf("Polling every %d seconds from kernel...\n", secs); + + /* Set high priority for this process, less chances to overrun + * the netlink receiver buffer since the scheduler gives this process + * more chances to run. + */ + nice(-20); + + /* Open netlink socket to operate with netfilter */ + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + /* Subscribe to destroy events to avoid leaking counters. The same + * socket is used to periodically atomically dump and reset counters. + */ + if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_DESTROY, + MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + /* Set netlink receiver buffer to 16 MBytes, to avoid packet drops */ + setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE, + &buffersize, sizeof(socklen_t)); + + /* The two tweaks below enable reliable event delivery, packets may + * be dropped if the netlink receiver buffer overruns. This happens ... + * + * a) if the kernel spams this user-space process until the receiver + * is filled. + * + * or: + * + * b) if the user-space process does not pull messages from the + * receiver buffer so often. + */ + mnl_socket_setsockopt(nl, NETLINK_BROADCAST_ERROR, &on, sizeof(int)); + mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &on, sizeof(int)); + + nlh = mnl_nlmsg_put_header(buf); + /* Counters are atomically zeroed in each dump */ + nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | + IPCTNL_MSG_CT_GET_CTRZERO; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = AF_INET; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + /* Filter by mark: We only want to dump entries whose mark is zero */ + mnl_attr_put_u32(nlh, CTA_MARK, htonl(0)); + mnl_attr_put_u32(nlh, CTA_MARK_MASK, htonl(0xffffffff)); + + while (1) { + int fd_max = mnl_socket_get_fd(nl); + fd_set readfds; + + /* Every N seconds ... */ + if (tv.tv_sec == 0 && tv.tv_usec == 0) { + /* ... request a fresh dump of the table from kernel */ + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret == -1) { + perror("mnl_socket_sendto"); + return -1; + } + tv.tv_sec = secs; + tv.tv_usec = 0; + + /* print the content of the list */ + LIST_FOREACH(cur, &nstats_head, list) { + char out[INET6_ADDRSTRLEN]; + + if (inet_ntop(cur->family, &cur->ip, out, sizeof(out))) + printf("src=%s ", out); + + printf("counters %"PRIu64" %"PRIu64"\n", + cur->pkts, cur->bytes); + } + } + + FD_ZERO(&readfds); + FD_SET(mnl_socket_get_fd(nl), &readfds); + + ret = select(fd_max+1, &readfds, NULL, NULL, &tv); + if (ret < 0) { + if (errno == EINTR) + continue; + + perror("select"); + exit(EXIT_FAILURE); + } + + /* Handled event and periodic atomic-dump-and-reset messages */ + if (FD_ISSET(mnl_socket_get_fd(nl), &readfds)) { + if (handle(nl) < 0) + return EXIT_FAILURE; + } + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/netfilter/nfct-dump.c b/libmnl/examples/netfilter/nfct-dump.c new file mode 100644 index 0000000..cb8e52c --- /dev/null +++ b/libmnl/examples/netfilter/nfct-dump.c @@ -0,0 +1,319 @@ +/* This example is placed in the public domain. */ +#include <endian.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_conntrack.h> + +static int parse_counters_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_COUNTERS_PACKETS: + case CTA_COUNTERS_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_counters(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_counters_cb, tb); + if (tb[CTA_COUNTERS_PACKETS]) { + printf("packets=%"PRIu64" ", + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS]))); + } + if (tb[CTA_COUNTERS_BYTES]) { + printf("bytes=%"PRIu64" ", + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES]))); + } +} + +static int parse_ip_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_IP_V4_SRC: + case CTA_IP_V4_DST: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_IP_V6_SRC: + case CTA_IP_V6_DST: + if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, + sizeof(struct in6_addr)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_ip(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_IP_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_ip_cb, tb); + if (tb[CTA_IP_V4_SRC]) { + struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]); + printf("src=%s ", inet_ntoa(*in)); + } + if (tb[CTA_IP_V4_DST]) { + struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_DST]); + printf("dst=%s ", inet_ntoa(*in)); + } + if (tb[CTA_IP_V6_SRC]) { + struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]); + char out[INET6_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET6, in, out, sizeof(out))) + printf("src=%s ", out); + } + if (tb[CTA_IP_V6_DST]) { + struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_DST]); + char out[INET6_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET6, in, out, sizeof(out))) + printf("dst=%s ", out); + } +} + +static int parse_proto_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTO_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTO_NUM: + case CTA_PROTO_ICMP_TYPE: + case CTA_PROTO_ICMP_CODE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_PROTO_SRC_PORT: + case CTA_PROTO_DST_PORT: + case CTA_PROTO_ICMP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_proto(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_PROTO_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_proto_cb, tb); + if (tb[CTA_PROTO_NUM]) { + printf("proto=%u ", mnl_attr_get_u8(tb[CTA_PROTO_NUM])); + } + if (tb[CTA_PROTO_SRC_PORT]) { + printf("sport=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_SRC_PORT]))); + } + if (tb[CTA_PROTO_DST_PORT]) { + printf("dport=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_DST_PORT]))); + } + if (tb[CTA_PROTO_ICMP_ID]) { + printf("id=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_ICMP_ID]))); + } + if (tb[CTA_PROTO_ICMP_TYPE]) { + printf("type=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_TYPE])); + } + if (tb[CTA_PROTO_ICMP_CODE]) { + printf("code=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_CODE])); + } +} + +static int parse_tuple_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_IP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_TUPLE_PROTO: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_tuple(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_tuple_cb, tb); + if (tb[CTA_TUPLE_IP]) { + print_ip(tb[CTA_TUPLE_IP]); + } + if (tb[CTA_TUPLE_PROTO]) { + print_proto(tb[CTA_TUPLE_PROTO]); + } +} + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_ORIG: + case CTA_COUNTERS_ORIG: + case CTA_COUNTERS_REPLY: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_TIMEOUT: + case CTA_MARK: + case CTA_SECMARK: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTA_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb); + if (tb[CTA_TUPLE_ORIG]) + print_tuple(tb[CTA_TUPLE_ORIG]); + + if (tb[CTA_MARK]) + printf("mark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_MARK]))); + + if (tb[CTA_SECMARK]) + printf("secmark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_SECMARK]))); + + if (tb[CTA_COUNTERS_ORIG]) { + printf("original "); + print_counters(tb[CTA_COUNTERS_ORIG]); + } + + if (tb[CTA_COUNTERS_REPLY]) { + printf("reply "); + print_counters(tb[CTA_COUNTERS_REPLY]); + } + + printf("\n"); + return MNL_CB_OK; +} + +int main(void) +{ + char buf[MNL_SOCKET_DUMP_SIZE]; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + uint32_t seq, portid; + int ret; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = AF_INET; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret == -1) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + while (1) { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } else if (ret <= MNL_CB_STOP) + break; + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/netfilter/nfct-event.c b/libmnl/examples/netfilter/nfct-event.c new file mode 100644 index 0000000..94603d4 --- /dev/null +++ b/libmnl/examples/netfilter/nfct-event.c @@ -0,0 +1,240 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_conntrack.h> + +static int parse_ip_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_IP_V4_SRC: + case CTA_IP_V4_DST: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_ip(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_IP_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_ip_cb, tb); + if (tb[CTA_IP_V4_SRC]) { + struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]); + printf("src=%s ", inet_ntoa(*in)); + } + if (tb[CTA_IP_V4_DST]) { + struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_DST]); + printf("dst=%s ", inet_ntoa(*in)); + } +} + +static int parse_proto_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTO_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTO_NUM: + case CTA_PROTO_ICMP_TYPE: + case CTA_PROTO_ICMP_CODE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_PROTO_SRC_PORT: + case CTA_PROTO_DST_PORT: + case CTA_PROTO_ICMP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_proto(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_PROTO_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_proto_cb, tb); + if (tb[CTA_PROTO_NUM]) { + printf("proto=%u ", mnl_attr_get_u8(tb[CTA_PROTO_NUM])); + } + if (tb[CTA_PROTO_SRC_PORT]) { + printf("sport=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_SRC_PORT]))); + } + if (tb[CTA_PROTO_DST_PORT]) { + printf("dport=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_DST_PORT]))); + } + if (tb[CTA_PROTO_ICMP_ID]) { + printf("id=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_ICMP_ID]))); + } + if (tb[CTA_PROTO_ICMP_TYPE]) { + printf("type=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_TYPE])); + } + if (tb[CTA_PROTO_ICMP_CODE]) { + printf("code=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_CODE])); + } +} + +static int parse_tuple_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_IP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_TUPLE_PROTO: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_tuple(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_tuple_cb, tb); + if (tb[CTA_TUPLE_IP]) { + print_ip(tb[CTA_TUPLE_IP]); + } + if (tb[CTA_TUPLE_PROTO]) { + print_proto(tb[CTA_TUPLE_PROTO]); + } +} + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_ORIG: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_TIMEOUT: + case CTA_MARK: + case CTA_SECMARK: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTA_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + switch(nlh->nlmsg_type & 0xFF) { + case IPCTNL_MSG_CT_NEW: + if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) + printf("%9s ", "[NEW] "); + else + printf("%9s ", "[UPDATE] "); + break; + case IPCTNL_MSG_CT_DELETE: + printf("%9s ", "[DESTROY] "); + break; + } + + mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb); + if (tb[CTA_TUPLE_ORIG]) { + print_tuple(tb[CTA_TUPLE_ORIG]); + } + if (tb[CTA_MARK]) { + printf("mark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_MARK]))); + } + if (tb[CTA_SECMARK]) { + printf("secmark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_SECMARK]))); + } + printf("\n"); + return MNL_CB_OK; +} + +int main(void) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_NEW | + NF_NETLINK_CONNTRACK_UPDATE | + NF_NETLINK_CONNTRACK_DESTROY, + MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + while (1) { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/.gitignore b/libmnl/examples/rtnl/.gitignore new file mode 100644 index 0000000..4b16c53 --- /dev/null +++ b/libmnl/examples/rtnl/.gitignore @@ -0,0 +1,12 @@ +/rtnl-addr-add +/rtnl-addr-dump +/rtnl-link-can +/rtnl-link-dump +/rtnl-link-dump2 +/rtnl-link-dump3 +/rtnl-link-event +/rtnl-link-set +/rtnl-neigh-dump +/rtnl-route-event +/rtnl-route-add +/rtnl-route-dump diff --git a/libmnl/examples/rtnl/Makefile.am b/libmnl/examples/rtnl/Makefile.am new file mode 100644 index 0000000..5a28e09 --- /dev/null +++ b/libmnl/examples/rtnl/Makefile.am @@ -0,0 +1,48 @@ +include $(top_srcdir)/Make_global.am + +check_PROGRAMS = rtnl-addr-add \ + rtnl-addr-dump \ + rtnl-link-can \ + rtnl-link-dump rtnl-link-dump2 rtnl-link-dump3 \ + rtnl-link-event \ + rtnl-link-set \ + rtnl-route-add \ + rtnl-route-dump \ + rtnl-route-event \ + rtnl-neigh-dump + +rtnl_addr_add_SOURCES = rtnl-addr-add.c +rtnl_addr_add_LDADD = ../../src/libmnl.la + +rtnl_link_can_SOURCES = rtnl-link-can.c +rtnl_link_can_LDADD = ../../src/libmnl.la + +rtnl_addr_dump_SOURCES = rtnl-addr-dump.c +rtnl_addr_dump_LDADD = ../../src/libmnl.la + +rtnl_link_dump_SOURCES = rtnl-link-dump.c +rtnl_link_dump_LDADD = ../../src/libmnl.la + +rtnl_link_dump2_SOURCES = rtnl-link-dump2.c +rtnl_link_dump2_LDADD = ../../src/libmnl.la + +rtnl_link_dump3_SOURCES = rtnl-link-dump3.c +rtnl_link_dump3_LDADD = ../../src/libmnl.la + +rtnl_route_add_SOURCES = rtnl-route-add.c +rtnl_route_add_LDADD = ../../src/libmnl.la + +rtnl_link_event_SOURCES = rtnl-link-event.c +rtnl_link_event_LDADD = ../../src/libmnl.la + +rtnl_link_set_SOURCES = rtnl-link-set.c +rtnl_link_set_LDADD = ../../src/libmnl.la + +rtnl_route_dump_SOURCES = rtnl-route-dump.c +rtnl_route_dump_LDADD = ../../src/libmnl.la + +rtnl_route_event_SOURCES = rtnl-route-event.c +rtnl_route_event_LDADD = ../../src/libmnl.la + +rtnl_neigh_dump_SOURCES = rtnl-neigh-dump.c +rtnl_neigh_dump_LDADD = ../../src/libmnl.la diff --git a/libmnl/examples/rtnl/rtnl-addr-add.c b/libmnl/examples/rtnl/rtnl-addr-add.c new file mode 100644 index 0000000..f9a69dc --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-addr-add.c @@ -0,0 +1,119 @@ +/* This example is placed in the public domain. */ +#include <netinet/in.h> +#include <arpa/inet.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <strings.h> +#include <net/if.h> + +#include <libmnl/libmnl.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifaddrmsg *ifm; + uint32_t seq, portid; + union { + in_addr_t ip; + struct in6_addr ip6; + } addr; + int ret, family = AF_INET; + + uint32_t prefix; + int iface; + + + if (argc <= 3) { + printf("Usage: %s iface destination cidr\n", argv[0]); + printf("Example: %s eth0 10.0.1.12 32\n", argv[0]); + printf(" %s eth0 ffff::10.0.1.12 128\n", argv[0]); + exit(EXIT_FAILURE); + } + + iface = if_nametoindex(argv[1]); + if (iface == 0) { + perror("if_nametoindex"); + exit(EXIT_FAILURE); + } + + if (!inet_pton(AF_INET, argv[2], &addr)) { + if (!inet_pton(AF_INET6, argv[2], &addr)) { + perror("inet_pton"); + exit(EXIT_FAILURE); + } + family = AF_INET6; + } + + if (sscanf(argv[3], "%u", &prefix) == 0) { + perror("sscanf"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWADDR; + + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); + + ifm->ifa_family = family; + ifm->ifa_prefixlen = prefix; + ifm->ifa_flags = IFA_F_PERMANENT; + + ifm->ifa_scope = RT_SCOPE_UNIVERSE; + ifm->ifa_index = iface; + + /* + * The exact meaning of IFA_LOCAL and IFA_ADDRESS depend + * on the address family being used and the device type. + * For broadcast devices (like the interfaces we use), + * for IPv4 we specify both and they are used interchangeably. + * For IPv6, only IFA_ADDRESS needs to be set. + */ + if (family == AF_INET) { + mnl_attr_put_u32(nlh, IFA_LOCAL, addr.ip); + mnl_attr_put_u32(nlh, IFA_ADDRESS, addr.ip); + } else { + mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), &addr); + } + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret < 0) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret < 0) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-addr-dump.c b/libmnl/examples/rtnl/rtnl-addr-dump.c new file mode 100644 index 0000000..675e9b0 --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-addr-dump.c @@ -0,0 +1,133 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case IFA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFA_MAX + 1] = {}; + struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); + + printf("index=%d family=%d ", ifa->ifa_index, ifa->ifa_family); + + mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb); + printf("addr="); + if (tb[IFA_ADDRESS]) { + void *addr = mnl_attr_get_payload(tb[IFA_ADDRESS]); + char out[INET6_ADDRSTRLEN]; + + if (inet_ntop(ifa->ifa_family, addr, out, sizeof(out))) + printf("%s ", out); + } + printf("scope="); + switch(ifa->ifa_scope) { + case 0: + printf("global "); + break; + case 200: + printf("site "); + break; + case 253: + printf("link "); + break; + case 254: + printf("host "); + break; + case 255: + printf("nowhere "); + break; + default: + printf("%d ", ifa->ifa_scope); + break; + } + + printf("\n"); + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + char buf[MNL_SOCKET_DUMP_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int ret; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + if (strcmp(argv[1], "inet") == 0) + rt->rtgen_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + rt->rtgen_family = AF_INET6; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-link-can.c b/libmnl/examples/rtnl/rtnl-link-can.c new file mode 100644 index 0000000..8ed70d1 --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-link-can.c @@ -0,0 +1,452 @@ +/* This example is placed in the public domain. */ +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#include <libmnl/libmnl.h> +#include <linux/can/netlink.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static void incomplete_command(void) __attribute__((noreturn)); + +#define NEXT_ARG() \ + do { \ + if (argc <= 0) incomplete_command(); \ + argv++; \ + argc--; \ + } while (0) + +static void duparg2(const char *key, const char *arg) +{ + fprintf(stderr, + "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n", + key, arg); + exit(-1); +} + +static void incomplete_command(void) +{ + fprintf(stderr, "Command line is not complete. Try option \"help\"\n"); + exit(EXIT_FAILURE); +} + +/* Returns false if 'prefix' is a not empty prefix of 'string'. + */ +static bool matches(const char *prefix, const char *string) +{ + if (!*prefix) + return true; + + while (*string && *prefix == *string) { + prefix++; + string++; + } + + return !!*prefix; +} + +static int get_u16(__u16 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + + res = strtoul(arg, &ptr, base); + + /* empty string or trailing non-digits */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* overflow */ + if (res == ULONG_MAX && errno == ERANGE) + return -1; + + if (res > 0xFFFFUL) + return -1; + + *val = res; + return 0; +} + +static int get_u32(__u32 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + + res = strtoul(arg, &ptr, base); + + /* empty string or trailing non-digits */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* overflow */ + if (res == ULONG_MAX && errno == ERANGE) + return -1; + + /* in case UL > 32 bits */ + if (res > 0xFFFFFFFFUL) + return -1; + + *val = res; + return 0; +} + +static int get_float(float *val, const char *arg) +{ + float res; + char *ptr; + + if (!arg || !*arg) + return -1; + + res = strtof(arg, &ptr); + if (!ptr || ptr == arg || *ptr) + return -1; + + *val = res; + return 0; +} + +static void set_ctrlmode(char *name, char *arg, + struct can_ctrlmode *cm, __u32 flags) +{ + if (strcmp(arg, "on") == 0) { + cm->flags |= flags; + } else if (strcmp(arg, "off") != 0) { + fprintf(stderr, + "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", + name, arg); + exit(EXIT_FAILURE); + } + + cm->mask |= flags; +} + +static void invarg(const char *msg, const char *arg) +{ + fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg); + exit(-1); +} + +static void print_usage(FILE *f) +{ + fprintf(f, + "Usage: ip link set DEVICE type can\n" + "\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] |\n" + "\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n \t phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n" + "\n" + "\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n" + "\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n" + "\n" + "\t[ loopback { on | off } ]\n" + "\t[ listen-only { on | off } ]\n" + "\t[ triple-sampling { on | off } ]\n" + "\t[ one-shot { on | off } ]\n" + "\t[ berr-reporting { on | off } ]\n" + "\t[ fd { on | off } ]\n" + "\t[ fd-non-iso { on | off } ]\n" + "\t[ presume-ack { on | off } ]\n" + "\t[ cc-len8-dlc { on | off } ]\n" + "\n" + "\t[ restart-ms TIME-MS ]\n" + "\t[ restart ]\n" + "\n" + "\t[ termination { 0..65535 } ]\n" + "\n" + "\tWhere: BITRATE := { 1..1000000 }\n" + "\t SAMPLE-POINT := { 0.000..0.999 }\n" + "\t TQ := { NUMBER }\n" + "\t PROP-SEG := { 1..8 }\n" + "\t PHASE-SEG1 := { 1..8 }\n" + "\t PHASE-SEG2 := { 1..8 }\n" + "\t SJW := { 1..4 }\n" + "\t RESTART-MS := { 0 | NUMBER }\n" + ); +} + +static void usage(void) +{ + print_usage(stderr); +} + +static int iplink_set_can_parse(int argc, char **argv, struct nlmsghdr *nlh) +{ + struct can_bittiming bt = {}, dbt = {}; + struct can_ctrlmode cm = {}; + + while (argc > 0) { + if (matches(*argv, "bitrate") == 0) { + NEXT_ARG(); + if (get_u32(&bt.bitrate, *argv, 0)) + invarg("invalid \"bitrate\" value\n", *argv); + } else if (matches(*argv, "sample-point") == 0) { + float sp; + + NEXT_ARG(); + if (get_float(&sp, *argv)) + invarg("invalid \"sample-point\" value\n", + *argv); + + bt.sample_point = (__u32)(sp * 1000); + } else if (matches(*argv, "tq") == 0) { + NEXT_ARG(); + if (get_u32(&bt.tq, *argv, 0)) + invarg("invalid \"tq\" value\n", *argv); + } else if (matches(*argv, "prop-seg") == 0) { + NEXT_ARG(); + if (get_u32(&bt.prop_seg, *argv, 0)) + invarg("invalid \"prop-seg\" value\n", *argv); + } else if (matches(*argv, "phase-seg1") == 0) { + NEXT_ARG(); + if (get_u32(&bt.phase_seg1, *argv, 0)) + invarg("invalid \"phase-seg1\" value\n", *argv); + } else if (matches(*argv, "phase-seg2") == 0) { + NEXT_ARG(); + if (get_u32(&bt.phase_seg2, *argv, 0)) + invarg("invalid \"phase-seg2\" value\n", *argv); + } else if (matches(*argv, "sjw") == 0) { + NEXT_ARG(); + if (get_u32(&bt.sjw, *argv, 0)) + invarg("invalid \"sjw\" value\n", *argv); + } else if (matches(*argv, "dbitrate") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.bitrate, *argv, 0)) + invarg("invalid \"dbitrate\" value\n", *argv); + } else if (matches(*argv, "dsample-point") == 0) { + float sp; + + NEXT_ARG(); + if (get_float(&sp, *argv)) + invarg("invalid \"dsample-point\" value\n", *argv); + dbt.sample_point = (__u32)(sp * 1000); + } else if (matches(*argv, "dtq") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.tq, *argv, 0)) + invarg("invalid \"dtq\" value\n", *argv); + } else if (matches(*argv, "dprop-seg") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.prop_seg, *argv, 0)) + invarg("invalid \"dprop-seg\" value\n", *argv); + } else if (matches(*argv, "dphase-seg1") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.phase_seg1, *argv, 0)) + invarg("invalid \"dphase-seg1\" value\n", *argv); + } else if (matches(*argv, "dphase-seg2") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.phase_seg2, *argv, 0)) + invarg("invalid \"dphase-seg2\" value\n", *argv); + } else if (matches(*argv, "dsjw") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.sjw, *argv, 0)) + invarg("invalid \"dsjw\" value\n", *argv); + } else if (matches(*argv, "loopback") == 0) { + NEXT_ARG(); + set_ctrlmode("loopback", *argv, &cm, + CAN_CTRLMODE_LOOPBACK); + } else if (matches(*argv, "listen-only") == 0) { + NEXT_ARG(); + set_ctrlmode("listen-only", *argv, &cm, + CAN_CTRLMODE_LISTENONLY); + } else if (matches(*argv, "triple-sampling") == 0) { + NEXT_ARG(); + set_ctrlmode("triple-sampling", *argv, &cm, + CAN_CTRLMODE_3_SAMPLES); + } else if (matches(*argv, "one-shot") == 0) { + NEXT_ARG(); + set_ctrlmode("one-shot", *argv, &cm, + CAN_CTRLMODE_ONE_SHOT); + } else if (matches(*argv, "berr-reporting") == 0) { + NEXT_ARG(); + set_ctrlmode("berr-reporting", *argv, &cm, + CAN_CTRLMODE_BERR_REPORTING); + } else if (matches(*argv, "fd") == 0) { + NEXT_ARG(); + set_ctrlmode("fd", *argv, &cm, + CAN_CTRLMODE_FD); + } else if (matches(*argv, "fd-non-iso") == 0) { + NEXT_ARG(); + set_ctrlmode("fd-non-iso", *argv, &cm, + CAN_CTRLMODE_FD_NON_ISO); + } else if (matches(*argv, "presume-ack") == 0) { + NEXT_ARG(); + set_ctrlmode("presume-ack", *argv, &cm, + CAN_CTRLMODE_PRESUME_ACK); +#if defined(CAN_CTRLMODE_CC_LEN8_DLC) + } else if (matches(*argv, "cc-len8-dlc") == 0) { + NEXT_ARG(); + set_ctrlmode("cc-len8-dlc", *argv, &cm, + CAN_CTRLMODE_CC_LEN8_DLC); +#endif + } else if (matches(*argv, "restart") == 0) { + __u32 val = 1; + + mnl_attr_put(nlh, IFLA_CAN_RESTART, sizeof(val), &val); + } else if (matches(*argv, "restart-ms") == 0) { + __u32 val; + + NEXT_ARG(); + if (get_u32(&val, *argv, 0)) + invarg("invalid \"restart-ms\" value\n", *argv); + + mnl_attr_put(nlh, IFLA_CAN_RESTART_MS, sizeof(val), &val); + } else if (matches(*argv, "termination") == 0) { + __u16 val; + + NEXT_ARG(); + if (get_u16(&val, *argv, 0)) + invarg("invalid \"termination\" value\n", + *argv); + + mnl_attr_put(nlh, IFLA_CAN_TERMINATION, sizeof(val), &val); + } else { + fprintf(stderr, "unknown option \"%s\"\n", *argv); + usage(); + return -1; + } + + NEXT_ARG(); + } + + if (bt.bitrate || bt.tq) + mnl_attr_put(nlh, IFLA_CAN_BITTIMING, sizeof(bt), &bt); + + if (cm.mask) + mnl_attr_put(nlh, IFLA_CAN_CTRLMODE, sizeof(cm), &cm); + + return 0; +} + +int main(int argc, char *argv[]) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + int ret; + unsigned int seq, portid; + struct nlattr *linkinfo, *data; + const char *signatures[] = { + "ip", "link", "set", "" + }; + char *type = NULL; + char *dev = NULL; + int i; + + NEXT_ARG(); + for (i = 0; argc > 0 && signatures[i][0];) { + if (matches(*argv, signatures[i])) + incomplete_command(); + + NEXT_ARG(); + i++; + } + + if (argc == 0) + incomplete_command(); + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_UNSPEC; + ifm->ifi_change = 0; + ifm->ifi_flags = 0; + + while (argc > 0) { + if (matches(*argv, "up") == 0) { + ifm->ifi_change |= IFF_UP; + ifm->ifi_flags |= IFF_UP; + } else if (matches(*argv, "down") == 0) { + ifm->ifi_change |= IFF_UP; + ifm->ifi_flags &= ~IFF_UP; + } else if (matches(*argv, "type") == 0) { + NEXT_ARG(); + type = *argv; + NEXT_ARG(); + break; + } else if (matches(*argv, "help") == 0) { + usage(); + exit(EXIT_FAILURE); + } else { + if (matches(*argv, "dev") == 0) + NEXT_ARG(); + + if (dev) + duparg2("dev", *argv); + + dev = *argv; + } + + NEXT_ARG(); + } + + if (dev) + mnl_attr_put_str(nlh, IFLA_IFNAME, dev); + + if (type) { + if (matches(type, "can")) { + fprintf(stderr, "unknown type \"%s\"\n", type); + usage(); + exit(EXIT_FAILURE); + } + + linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO); + mnl_attr_put_str(nlh, IFLA_INFO_KIND, "can"); + data = mnl_attr_nest_start(nlh, IFLA_INFO_DATA); + + if (iplink_set_can_parse(argc, argv, nlh)) + return -1; + + mnl_attr_nest_end(nlh, data); + mnl_attr_nest_end(nlh, linkinfo); + } + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + portid = mnl_socket_get_portid(nl); + + mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, + sizeof(struct ifinfomsg)); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-link-dump.c b/libmnl/examples/rtnl/rtnl-link-dump.c new file mode 100644 index 0000000..031346f --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-link-dump.c @@ -0,0 +1,130 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case IFLA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case IFLA_MTU: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFLA_MAX+1] = {}; + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + + printf("index=%d type=%d flags=%d family=%d ", + ifm->ifi_index, ifm->ifi_type, + ifm->ifi_flags, ifm->ifi_family); + + if (ifm->ifi_flags & IFF_RUNNING) + printf("[RUNNING] "); + else + printf("[NOT RUNNING] "); + + mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb); + if (tb[IFLA_MTU]) { + printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU])); + } + if (tb[IFLA_IFNAME]) { + printf("name=%s ", mnl_attr_get_str(tb[IFLA_IFNAME])); + } + if (tb[IFLA_ADDRESS]) { + uint8_t *hwaddr = mnl_attr_get_payload(tb[IFLA_ADDRESS]); + int i; + + printf("hwaddr="); + for (i=0; i<mnl_attr_get_payload_len(tb[IFLA_ADDRESS]); i++) { + printf("%.2x", hwaddr[i] & 0xff); + if (i+1 != mnl_attr_get_payload_len(tb[IFLA_ADDRESS])) + printf(":"); + } + } + printf("\n"); + return MNL_CB_OK; +} + +int main(void) +{ + char buf[MNL_SOCKET_DUMP_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int ret; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_PACKET; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-link-dump2.c b/libmnl/examples/rtnl/rtnl-link-dump2.c new file mode 100644 index 0000000..890e51a --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-link-dump2.c @@ -0,0 +1,103 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + return MNL_CB_OK; + + switch(mnl_attr_get_type(attr)) { + case IFLA_MTU: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + printf("mtu=%d ", mnl_attr_get_u32(attr)); + break; + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + printf("name=%s ", mnl_attr_get_str(attr)); + break; + } + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + + printf("index=%d type=%d flags=%d family=%d ", + ifm->ifi_index, ifm->ifi_type, + ifm->ifi_flags, ifm->ifi_family); + + if (ifm->ifi_flags & IFF_RUNNING) + printf("[RUNNING] "); + else + printf("[NOT RUNNING] "); + + mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, NULL); + printf("\n"); + return MNL_CB_OK; +} + +int main(void) +{ + char buf[MNL_SOCKET_DUMP_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int ret; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_PACKET; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-link-dump3.c b/libmnl/examples/rtnl/rtnl-link-dump3.c new file mode 100644 index 0000000..a381da1 --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-link-dump3.c @@ -0,0 +1,103 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + struct nlattr *attr; + + printf("index=%d type=%d flags=%d family=%d ", + ifm->ifi_index, ifm->ifi_type, + ifm->ifi_flags, ifm->ifi_family); + + if (ifm->ifi_flags & IFF_RUNNING) + printf("[RUNNING] "); + else + printf("[NOT RUNNING] "); + + mnl_attr_for_each(attr, nlh, sizeof(*ifm)) { + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + continue; + + switch(type) { + case IFLA_MTU: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + printf("mtu=%d ", mnl_attr_get_u32(attr)); + break; + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + printf("name=%s ", mnl_attr_get_str(attr)); + break; + } + } + printf("\n"); + + return MNL_CB_OK; +} + +int main(void) +{ + char buf[MNL_SOCKET_DUMP_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int ret; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_PACKET; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-link-event.c b/libmnl/examples/rtnl/rtnl-link-event.c new file mode 100644 index 0000000..123fb88 --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-link-event.c @@ -0,0 +1,95 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case IFLA_MTU: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFLA_MAX+1] = {}; + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + + printf("index=%d type=%d flags=%d family=%d ", + ifm->ifi_index, ifm->ifi_type, + ifm->ifi_flags, ifm->ifi_family); + + if (ifm->ifi_flags & IFF_RUNNING) + printf("[RUNNING] "); + else + printf("[NOT RUNNING] "); + + mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb); + if (tb[IFLA_MTU]) { + printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU])); + } + if (tb[IFLA_IFNAME]) { + printf("name=%s", mnl_attr_get_str(tb[IFLA_IFNAME])); + } + printf("\n"); + return MNL_CB_OK; +} + +int main(void) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, RTMGRP_LINK, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-link-set.c b/libmnl/examples/rtnl/rtnl-link-set.c new file mode 100644 index 0000000..6086c37 --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-link-set.c @@ -0,0 +1,84 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + int ret; + unsigned int seq, portid, change = 0, flags = 0; + + if (argc != 3) { + printf("Usage: %s [ifname] [up|down]\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (strncasecmp(argv[2], "up", strlen("up")) == 0) { + change |= IFF_UP; + flags |= IFF_UP; + } else if (strncasecmp(argv[2], "down", strlen("down")) == 0) { + change |= IFF_UP; + flags &= ~IFF_UP; + } else { + fprintf(stderr, "%s is not `up' nor `down'\n", argv[2]); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_UNSPEC; + ifm->ifi_change = change; + ifm->ifi_flags = flags; + + mnl_attr_put_str(nlh, IFLA_IFNAME, argv[1]); + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, + sizeof(struct ifinfomsg)); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret == -1){ + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-neigh-dump.c b/libmnl/examples/rtnl/rtnl-neigh-dump.c new file mode 100644 index 0000000..8dd5f7e --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-neigh-dump.c @@ -0,0 +1,158 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, NDA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NDA_DST: + case NDA_LLADDR: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[NDA_MAX + 1] = {}; + struct ndmsg *ndm = mnl_nlmsg_get_payload(nlh); + + printf("index=%d family=%d ", ndm->ndm_ifindex, ndm->ndm_family); + + mnl_attr_parse(nlh, sizeof(*ndm), data_attr_cb, tb); + printf("dst="); + if (tb[NDA_DST]) { + void *addr = mnl_attr_get_payload(tb[NDA_DST]); + char out[INET6_ADDRSTRLEN]; + + if (inet_ntop(ndm->ndm_family, addr, out, sizeof(out))) + printf("%s ", out); + } + + mnl_attr_parse(nlh, sizeof(*ndm), data_attr_cb, tb); + printf("lladdr="); + if (tb[NDA_LLADDR]) { + void *addr = mnl_attr_get_payload(tb[NDA_LLADDR]); + unsigned char lladdr[6] = {0}; + + if (memcpy(&lladdr, addr, 6)) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + lladdr[0], lladdr[1], lladdr[2], + lladdr[3], lladdr[4], lladdr[5]); + } + + printf("state="); + switch(ndm->ndm_state) { + case NUD_INCOMPLETE: + printf("incomplete "); + break; + case NUD_REACHABLE: + printf("reachable "); + break; + case NUD_STALE: + printf("stale "); + break; + case NUD_DELAY: + printf("delay "); + break; + case NUD_PROBE: + printf("probe "); + break; + case NUD_FAILED: + printf("failed "); + break; + case NUD_NOARP: + printf("noarp "); + break; + case NUD_PERMANENT: + printf("permanent "); + break; + default: + printf("%d ", ndm->ndm_state); + break; + } + + printf("\n"); + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + char buf[MNL_SOCKET_DUMP_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct ndmsg *nd; + int ret; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETNEIGH; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + + nd = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ndmsg)); + if (strcmp(argv[1], "inet") == 0) + nd->ndm_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + nd->ndm_family = AF_INET6; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-route-add.c b/libmnl/examples/rtnl/rtnl-route-add.c new file mode 100644 index 0000000..97578cd --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-route-add.c @@ -0,0 +1,127 @@ +/* This example is placed in the public domain. */ +#include <netinet/in.h> +#include <arpa/inet.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <strings.h> +#include <net/if.h> + +#include <libmnl/libmnl.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtmsg *rtm; + uint32_t prefix, seq, portid; + union { + in_addr_t ip; + struct in6_addr ip6; + } dst; + union { + in_addr_t ip; + struct in6_addr ip6; + } gw; + int iface, ret, family = AF_INET; + + if (argc <= 3) { + printf("Usage: %s iface destination cidr [gateway]\n", argv[0]); + printf("Example: %s eth0 10.0.1.12 32 10.0.1.11\n", argv[0]); + printf(" %s eth0 ffff::10.0.1.12 128 fdff::1\n", argv[0]); + exit(EXIT_FAILURE); + } + + iface = if_nametoindex(argv[1]); + if (iface == 0) { + perror("if_nametoindex"); + exit(EXIT_FAILURE); + } + + if (!inet_pton(AF_INET, argv[2], &dst)) { + if (!inet_pton(AF_INET6, argv[2], &dst)) { + perror("inet_pton"); + exit(EXIT_FAILURE); + } + family = AF_INET6; + } + + if (sscanf(argv[3], "%u", &prefix) == 0) { + perror("sscanf"); + exit(EXIT_FAILURE); + } + + if (argc == 5 && !inet_pton(family, argv[4], &gw)) { + perror("inet_pton"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWROUTE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); + rtm->rtm_family = family; + rtm->rtm_dst_len = prefix; + rtm->rtm_src_len = 0; + rtm->rtm_tos = 0; + rtm->rtm_protocol = RTPROT_STATIC; + rtm->rtm_table = RT_TABLE_MAIN; + rtm->rtm_type = RTN_UNICAST; + /* is there any gateway? */ + rtm->rtm_scope = (argc == 4) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; + rtm->rtm_flags = 0; + + if (family == AF_INET) + mnl_attr_put_u32(nlh, RTA_DST, dst.ip); + else + mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst); + + mnl_attr_put_u32(nlh, RTA_OIF, iface); + if (argc == 5) { + if (family == AF_INET) + mnl_attr_put_u32(nlh, RTA_GATEWAY, gw.ip); + else { + mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), + &gw.ip6); + } + } + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret < 0) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret < 0) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-route-dump.c b/libmnl/examples/rtnl/rtnl-route-dump.c new file mode 100644 index 0000000..02ac6b2 --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-route-dump.c @@ -0,0 +1,356 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb2(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTAX_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + + tb[mnl_attr_get_type(attr)] = attr; + return MNL_CB_OK; +} + +static void attributes_show_ipv4(struct nlattr *tb[]) +{ + if (tb[RTA_TABLE]) { + printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE])); + } + if (tb[RTA_DST]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_DST]); + printf("dst=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_SRC]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]); + printf("src=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_OIF]) { + printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF])); + } + if (tb[RTA_FLOW]) { + printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW])); + } + if (tb[RTA_PREFSRC]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]); + printf("prefsrc=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_GATEWAY]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]); + printf("gw=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_PRIORITY]) { + printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY])); + } + if (tb[RTA_METRICS]) { + int i; + struct nlattr *tbx[RTAX_MAX+1] = {}; + + mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx); + + for (i=0; i<RTAX_MAX; i++) { + if (tbx[i]) { + printf("metrics[%d]=%u ", + i, mnl_attr_get_u32(tbx[i])); + } + } + } +} + +/* like inet_ntoa(), not reentrant */ +static const char *inet6_ntoa(struct in6_addr in6) +{ + static char buf[INET6_ADDRSTRLEN]; + + return inet_ntop(AF_INET6, &in6.s6_addr, buf, sizeof(buf)); +} + +static void attributes_show_ipv6(struct nlattr *tb[]) +{ + if (tb[RTA_TABLE]) { + printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE])); + } + if (tb[RTA_DST]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_DST]); + printf("dst=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_SRC]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]); + printf("src=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_OIF]) { + printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF])); + } + if (tb[RTA_FLOW]) { + printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW])); + } + if (tb[RTA_PREFSRC]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]); + printf("prefsrc=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_GATEWAY]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]); + printf("gw=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_PRIORITY]) { + printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY])); + } + if (tb[RTA_METRICS]) { + int i; + struct nlattr *tbx[RTAX_MAX+1] = {}; + + mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx); + + for (i=0; i<RTAX_MAX; i++) { + if (tbx[i]) { + printf("metrics[%d]=%u ", + i, mnl_attr_get_u32(tbx[i])); + } + } + } +} + +static int data_ipv4_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case RTA_TABLE: + case RTA_DST: + case RTA_SRC: + case RTA_OIF: + case RTA_FLOW: + case RTA_PREFSRC: + case RTA_GATEWAY: + case RTA_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case RTA_METRICS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_ipv6_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case RTA_TABLE: + case RTA_OIF: + case RTA_FLOW: + case RTA_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case RTA_DST: + case RTA_SRC: + case RTA_PREFSRC: + case RTA_GATEWAY: + if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, + sizeof(struct in6_addr)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case RTA_METRICS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RTA_MAX+1] = {}; + struct rtmsg *rm = mnl_nlmsg_get_payload(nlh); + + /* protocol family = AF_INET | AF_INET6 */ + printf("family=%u ", rm->rtm_family); + + /* destination CIDR, eg. 24 or 32 for IPv4 */ + printf("dst_len=%u ", rm->rtm_dst_len); + + /* source CIDR */ + printf("src_len=%u ", rm->rtm_src_len); + + /* type of service (TOS), eg. 0 */ + printf("tos=%u ", rm->rtm_tos); + + /* table id: + * RT_TABLE_UNSPEC = 0 + * + * ... user defined values ... + * + * RT_TABLE_COMPAT = 252 + * RT_TABLE_DEFAULT = 253 + * RT_TABLE_MAIN = 254 + * RT_TABLE_LOCAL = 255 + * RT_TABLE_MAX = 0xFFFFFFFF + * + * Synonimous attribute: RTA_TABLE. + */ + printf("table=%u ", rm->rtm_table); + + /* type: + * RTN_UNSPEC = 0 + * RTN_UNICAST = 1 + * RTN_LOCAL = 2 + * RTN_BROADCAST = 3 + * RTN_ANYCAST = 4 + * RTN_MULTICAST = 5 + * RTN_BLACKHOLE = 6 + * RTN_UNREACHABLE = 7 + * RTN_PROHIBIT = 8 + * RTN_THROW = 9 + * RTN_NAT = 10 + * RTN_XRESOLVE = 11 + * __RTN_MAX = 12 + */ + printf("type=%u ", rm->rtm_type); + + /* scope: + * RT_SCOPE_UNIVERSE = 0 : everywhere in the universe + * + * ... user defined values ... + * + * RT_SCOPE_SITE = 200 + * RT_SCOPE_LINK = 253 : destination attached to link + * RT_SCOPE_HOST = 254 : local address + * RT_SCOPE_NOWHERE = 255 : not existing destination + */ + printf("scope=%u ", rm->rtm_scope); + + /* protocol: + * RTPROT_UNSPEC = 0 + * RTPROT_REDIRECT = 1 + * RTPROT_KERNEL = 2 : route installed by kernel + * RTPROT_BOOT = 3 : route installed during boot + * RTPROT_STATIC = 4 : route installed by administrator + * + * Values >= RTPROT_STATIC are not interpreted by kernel, they are + * just user-defined. + */ + printf("proto=%u ", rm->rtm_protocol); + + /* flags: + * RTM_F_NOTIFY = 0x100: notify user of route change + * RTM_F_CLONED = 0x200: this route is cloned + * RTM_F_EQUALIZE = 0x400: Multipath equalizer: NI + * RTM_F_PREFIX = 0x800: Prefix addresses + */ + printf("flags=%x ", rm->rtm_flags); + + switch(rm->rtm_family) { + case AF_INET: + mnl_attr_parse(nlh, sizeof(*rm), data_ipv4_attr_cb, tb); + attributes_show_ipv4(tb); + break; + case AF_INET6: + mnl_attr_parse(nlh, sizeof(*rm), data_ipv6_attr_cb, tb); + attributes_show_ipv6(tb); + break; + } + + printf("\n"); + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + char buf[MNL_SOCKET_DUMP_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct rtmsg *rtm; + int ret; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETROUTE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); + + if (strcmp(argv[1], "inet") == 0) + rtm->rtm_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + rtm->rtm_family = AF_INET6; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/examples/rtnl/rtnl-route-event.c b/libmnl/examples/rtnl/rtnl-route-event.c new file mode 100644 index 0000000..6cad9f0 --- /dev/null +++ b/libmnl/examples/rtnl/rtnl-route-event.c @@ -0,0 +1,341 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb2(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTAX_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + + tb[mnl_attr_get_type(attr)] = attr; + return MNL_CB_OK; +} + +static void attributes_show_ipv4(struct nlattr *tb[]) +{ + if (tb[RTA_TABLE]) { + printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE])); + } + if (tb[RTA_DST]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_DST]); + printf("dst=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_SRC]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]); + printf("src=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_OIF]) { + printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF])); + } + if (tb[RTA_FLOW]) { + printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW])); + } + if (tb[RTA_PREFSRC]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]); + printf("prefsrc=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_GATEWAY]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]); + printf("gw=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_PRIORITY]) { + printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY])); + } + if (tb[RTA_METRICS]) { + int i; + struct nlattr *tbx[RTAX_MAX+1] = {}; + + mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx); + + for (i=0; i<RTAX_MAX; i++) { + if (tbx[i]) { + printf("metrics[%d]=%u ", + i, mnl_attr_get_u32(tbx[i])); + } + } + } +} + +/* like inet_ntoa(), not reentrant */ +static const char *inet6_ntoa(struct in6_addr in6) +{ + static char buf[INET6_ADDRSTRLEN]; + + return inet_ntop(AF_INET6, &in6.s6_addr, buf, sizeof(buf)); +} + +static void attributes_show_ipv6(struct nlattr *tb[]) +{ + if (tb[RTA_TABLE]) { + printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE])); + } + if (tb[RTA_DST]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_DST]); + printf("dst=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_SRC]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]); + printf("src=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_OIF]) { + printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF])); + } + if (tb[RTA_FLOW]) { + printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW])); + } + if (tb[RTA_PREFSRC]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]); + printf("prefsrc=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_GATEWAY]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]); + printf("gw=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_PRIORITY]) { + printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY])); + } + if (tb[RTA_METRICS]) { + int i; + struct nlattr *tbx[RTAX_MAX+1] = {}; + + mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx); + + for (i=0; i<RTAX_MAX; i++) { + if (tbx[i]) { + printf("metrics[%d]=%u ", + i, mnl_attr_get_u32(tbx[i])); + } + } + } +} + +static int data_ipv4_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case RTA_TABLE: + case RTA_DST: + case RTA_SRC: + case RTA_OIF: + case RTA_FLOW: + case RTA_PREFSRC: + case RTA_GATEWAY: + case RTA_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case RTA_METRICS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_ipv6_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case RTA_TABLE: + case RTA_OIF: + case RTA_FLOW: + case RTA_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case RTA_DST: + case RTA_SRC: + case RTA_PREFSRC: + case RTA_GATEWAY: + if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, + sizeof(struct in6_addr)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case RTA_METRICS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RTA_MAX+1] = {}; + struct rtmsg *rm = mnl_nlmsg_get_payload(nlh); + + switch(nlh->nlmsg_type) { + case RTM_NEWROUTE: + printf("[NEW] "); + break; + case RTM_DELROUTE: + printf("[DEL] "); + break; + } + + /* protocol family = AF_INET | AF_INET6 */ + printf("family=%u ", rm->rtm_family); + + /* destination CIDR, eg. 24 or 32 for IPv4 */ + printf("dst_len=%u ", rm->rtm_dst_len); + + /* source CIDR */ + printf("src_len=%u ", rm->rtm_src_len); + + /* type of service (TOS), eg. 0 */ + printf("tos=%u ", rm->rtm_tos); + + /* table id: + * RT_TABLE_UNSPEC = 0 + * + * ... user defined values ... + * + * RT_TABLE_COMPAT = 252 + * RT_TABLE_DEFAULT = 253 + * RT_TABLE_MAIN = 254 + * RT_TABLE_LOCAL = 255 + * RT_TABLE_MAX = 0xFFFFFFFF + * + * Synonimous attribute: RTA_TABLE. + */ + printf("table=%u ", rm->rtm_table); + + /* type: + * RTN_UNSPEC = 0 + * RTN_UNICAST = 1 + * RTN_LOCAL = 2 + * RTN_BROADCAST = 3 + * RTN_ANYCAST = 4 + * RTN_MULTICAST = 5 + * RTN_BLACKHOLE = 6 + * RTN_UNREACHABLE = 7 + * RTN_PROHIBIT = 8 + * RTN_THROW = 9 + * RTN_NAT = 10 + * RTN_XRESOLVE = 11 + * __RTN_MAX = 12 + */ + printf("type=%u ", rm->rtm_type); + + /* scope: + * RT_SCOPE_UNIVERSE = 0 : everywhere in the universe + * + * ... user defined values ... + * + * RT_SCOPE_SITE = 200 + * RT_SCOPE_LINK = 253 : destination attached to link + * RT_SCOPE_HOST = 254 : local address + * RT_SCOPE_NOWHERE = 255 : not existing destination + */ + printf("scope=%u ", rm->rtm_scope); + + /* protocol: + * RTPROT_UNSPEC = 0 + * RTPROT_REDIRECT = 1 + * RTPROT_KERNEL = 2 : route installed by kernel + * RTPROT_BOOT = 3 : route installed during boot + * RTPROT_STATIC = 4 : route installed by administrator + * + * Values >= RTPROT_STATIC are not interpreted by kernel, they are + * just user-defined. + */ + printf("proto=%u ", rm->rtm_protocol); + + /* flags: + * RTM_F_NOTIFY = 0x100: notify user of route change + * RTM_F_CLONED = 0x200: this route is cloned + * RTM_F_EQUALIZE = 0x400: Multipath equalizer: NI + * RTM_F_PREFIX = 0x800: Prefix addresses + */ + printf("flags=%x ", rm->rtm_flags); + + switch(rm->rtm_family) { + case AF_INET: + mnl_attr_parse(nlh, sizeof(*rm), data_ipv4_attr_cb, tb); + attributes_show_ipv4(tb); + break; + case AF_INET6: + mnl_attr_parse(nlh, sizeof(*rm), data_ipv6_attr_cb, tb); + attributes_show_ipv6(tb); + break; + } + + printf("\n"); + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE, + MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/libmnl/include/Makefile.am b/libmnl/include/Makefile.am new file mode 100644 index 0000000..bc92c42 --- /dev/null +++ b/libmnl/include/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = libmnl linux diff --git a/libmnl/include/libmnl/Makefile.am b/libmnl/include/libmnl/Makefile.am new file mode 100644 index 0000000..b03f68a --- /dev/null +++ b/libmnl/include/libmnl/Makefile.am @@ -0,0 +1 @@ +pkginclude_HEADERS = libmnl.h diff --git a/libmnl/include/libmnl/libmnl.h b/libmnl/include/libmnl/libmnl.h new file mode 100644 index 0000000..4bd0b92 --- /dev/null +++ b/libmnl/include/libmnl/libmnl.h @@ -0,0 +1,202 @@ +#ifndef _LIBMNL_H_ +#define _LIBMNL_H_ + +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <sys/socket.h> /* for sa_family_t */ +#include <linux/netlink.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Netlink socket API + */ + +#define MNL_SOCKET_AUTOPID 0 +#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L) +#define MNL_SOCKET_DUMP_SIZE 32768 + +struct mnl_socket; + +extern struct mnl_socket *mnl_socket_open(int bus); +extern struct mnl_socket *mnl_socket_open2(int bus, int flags); +extern struct mnl_socket *mnl_socket_fdopen(int fd); +extern int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid); +extern int mnl_socket_close(struct mnl_socket *nl); +extern int mnl_socket_get_fd(const struct mnl_socket *nl); +extern unsigned int mnl_socket_get_portid(const struct mnl_socket *nl); +extern ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t siz); +extern ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t siz); +extern int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t len); +extern int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t *len); + +/* + * Netlink message API + */ + +#define MNL_ALIGNTO 4 +#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1)) +#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr)) + +extern size_t mnl_nlmsg_size(size_t len); +extern size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh); + +/* Netlink message header builder */ +extern struct nlmsghdr *mnl_nlmsg_put_header(void *buf); +extern void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size); + +/* Netlink message iterators */ +extern bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len); +extern struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len); + +/* Netlink sequence tracking */ +extern bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq); + +/* Netlink portID checking */ +extern bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid); + +/* Netlink message getters */ +extern void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh); +extern void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset); +extern void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh); + +/* Netlink message printer */ +extern void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen, size_t extra_header_size); + +/* Message batch helpers */ +struct mnl_nlmsg_batch; +extern struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, size_t bufsiz); +extern bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b); +extern void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b); +extern size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b); +extern void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b); +extern void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b); +extern void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b); +extern bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b); + +/* + * Netlink attributes API + */ +#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr)) + +/* TLV attribute getters */ +extern uint16_t mnl_attr_get_type(const struct nlattr *attr); +extern uint16_t mnl_attr_get_len(const struct nlattr *attr); +extern uint16_t mnl_attr_get_payload_len(const struct nlattr *attr); +extern void *mnl_attr_get_payload(const struct nlattr *attr); +extern uint8_t mnl_attr_get_u8(const struct nlattr *attr); +extern uint16_t mnl_attr_get_u16(const struct nlattr *attr); +extern uint32_t mnl_attr_get_u32(const struct nlattr *attr); +extern uint64_t mnl_attr_get_u64(const struct nlattr *attr); +extern const char *mnl_attr_get_str(const struct nlattr *attr); + +/* TLV attribute putters */ +extern void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data); +extern void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, uint8_t data); +extern void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data); +extern void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data); +extern void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, uint64_t data); +extern void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, const char *data); +extern void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data); + +/* TLV attribute putters with buffer boundary checkings */ +extern bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, size_t len, const void *data); +extern bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint8_t data); +extern bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint16_t data); +extern bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint32_t data); +extern bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint64_t data); +extern bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data); +extern bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data); + +/* TLV attribute nesting */ +extern struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type); +extern struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type); +extern void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start); +extern void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start); + +/* TLV validation */ +extern int mnl_attr_type_valid(const struct nlattr *attr, uint16_t maxtype); + +enum mnl_attr_data_type { + MNL_TYPE_UNSPEC, + MNL_TYPE_U8, + MNL_TYPE_U16, + MNL_TYPE_U32, + MNL_TYPE_U64, + MNL_TYPE_STRING, + MNL_TYPE_FLAG, + MNL_TYPE_MSECS, + MNL_TYPE_NESTED, + MNL_TYPE_NESTED_COMPAT, + MNL_TYPE_NUL_STRING, + MNL_TYPE_BINARY, + MNL_TYPE_MAX, +}; + +extern int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type); +extern int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type, size_t len); + +/* TLV iterators */ +extern bool mnl_attr_ok(const struct nlattr *attr, int len); +extern struct nlattr *mnl_attr_next(const struct nlattr *attr); + +#define mnl_attr_for_each(attr, nlh, offset) \ + for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \ + mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \ + (attr) = mnl_attr_next(attr)) + +#define mnl_attr_for_each_nested(attr, nest) \ + for ((attr) = mnl_attr_get_payload(nest); \ + mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \ + (attr) = mnl_attr_next(attr)) + +#define mnl_attr_for_each_payload(payload, payload_size) \ + for ((attr) = (payload); \ + mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \ + (attr) = mnl_attr_next(attr)) + +/* TLV callback-based attribute parsers */ +typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data); + +extern int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset, mnl_attr_cb_t cb, void *data); +extern int mnl_attr_parse_nested(const struct nlattr *attr, mnl_attr_cb_t cb, void *data); +extern int mnl_attr_parse_payload(const void *payload, size_t payload_len, mnl_attr_cb_t cb, void *data); + +/* + * callback API + */ +#define MNL_CB_ERROR -1 +#define MNL_CB_STOP 0 +#define MNL_CB_OK 1 + +typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data); + +extern int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq, + unsigned int portid, mnl_cb_t cb_data, void *data); + +extern int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq, + unsigned int portid, mnl_cb_t cb_data, void *data, + const mnl_cb_t *cb_ctl_array, + unsigned int cb_ctl_array_len); + +/* + * other declarations + */ + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#ifndef MNL_ARRAY_SIZE +#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/libmnl/include/linux/Makefile.am b/libmnl/include/linux/Makefile.am new file mode 100644 index 0000000..ee0993d --- /dev/null +++ b/libmnl/include/linux/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = can netfilter +noinst_HEADERS = can.h netlink.h socket.h diff --git a/libmnl/include/linux/can.h b/libmnl/include/linux/can.h new file mode 100644 index 0000000..2c6a3ee --- /dev/null +++ b/libmnl/include/linux/can.h @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * linux/can.h + * + * Definitions for CAN network layer (socket addr / CAN frame / CAN filter) + * + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> + * Urs Thuermann <urs.thuermann@volkswagen.de> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#ifndef _UAPI_CAN_H +#define _UAPI_CAN_H + +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/stddef.h> /* for offsetof */ + +/* controller area network (CAN) kernel definitions */ + +/* special address description flags for the CAN_ID */ +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ +#define CAN_ERR_FLAG 0x20000000U /* error message frame */ + +/* valid bits in CAN ID for frame formats */ +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */ +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ +#define CANXL_PRIO_MASK CAN_SFF_MASK /* 11 bit priority mask */ + +/* + * Controller Area Network Identifier structure + * + * bit 0-28 : CAN identifier (11/29 bit) + * bit 29 : error message frame flag (0 = data frame, 1 = error message) + * bit 30 : remote transmission request flag (1 = rtr frame) + * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit) + */ +typedef __u32 canid_t; + +#define CAN_SFF_ID_BITS 11 +#define CAN_EFF_ID_BITS 29 +#define CANXL_PRIO_BITS CAN_SFF_ID_BITS + +/* + * Controller Area Network Error Message Frame Mask structure + * + * bit 0-28 : error class mask (see include/uapi/linux/can/error.h) + * bit 29-31 : set to zero + */ +typedef __u32 can_err_mask_t; + +/* CAN payload length and DLC definitions according to ISO 11898-1 */ +#define CAN_MAX_DLC 8 +#define CAN_MAX_RAW_DLC 15 +#define CAN_MAX_DLEN 8 + +/* CAN FD payload length and DLC definitions according to ISO 11898-7 */ +#define CANFD_MAX_DLC 15 +#define CANFD_MAX_DLEN 64 + +/* + * CAN XL payload length and DLC definitions according to ISO 11898-1 + * CAN XL DLC ranges from 0 .. 2047 => data length from 1 .. 2048 byte + */ +#define CANXL_MIN_DLC 0 +#define CANXL_MAX_DLC 2047 +#define CANXL_MAX_DLC_MASK 0x07FF +#define CANXL_MIN_DLEN 1 +#define CANXL_MAX_DLEN 2048 + +/** + * struct can_frame - Classical CAN frame structure (aka CAN 2.0B) + * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition + * @len: CAN frame payload length in byte (0 .. 8) + * @can_dlc: deprecated name for CAN frame payload length in byte (0 .. 8) + * @__pad: padding + * @__res0: reserved / padding + * @len8_dlc: optional DLC value (9 .. 15) at 8 byte payload length + * len8_dlc contains values from 9 .. 15 when the payload length is + * 8 bytes but the DLC value (see ISO 11898-1) is greater then 8. + * CAN_CTRLMODE_CC_LEN8_DLC flag has to be enabled in CAN driver. + * @data: CAN frame payload (up to 8 byte) + */ +struct can_frame { + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ + union { + /* CAN frame payload length in byte (0 .. CAN_MAX_DLEN) + * was previously named can_dlc so we need to carry that + * name for legacy support + */ + __u8 len; + __u8 can_dlc; /* deprecated */ + } __attribute__((packed)); /* disable padding added in some ABIs */ + __u8 __pad; /* padding */ + __u8 __res0; /* reserved / padding */ + __u8 len8_dlc; /* optional DLC for 8 byte payload length (9 .. 15) */ + __u8 data[CAN_MAX_DLEN] __attribute__((aligned(8))); +}; + +/* + * defined bits for canfd_frame.flags + * + * The use of struct canfd_frame implies the FD Frame (FDF) bit to + * be set in the CAN frame bitstream on the wire. The FDF bit switch turns + * the CAN controllers bitstream processor into the CAN FD mode which creates + * two new options within the CAN FD frame specification: + * + * Bit Rate Switch - to indicate a second bitrate is/was used for the payload + * Error State Indicator - represents the error state of the transmitting node + * + * As the CANFD_ESI bit is internally generated by the transmitting CAN + * controller only the CANFD_BRS bit is relevant for real CAN controllers when + * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make + * sense for virtual CAN interfaces to test applications with echoed frames. + * + * The struct can_frame and struct canfd_frame intentionally share the same + * layout to be able to write CAN frame content into a CAN FD frame structure. + * When this is done the former differentiation via CAN_MTU / CANFD_MTU gets + * lost. CANFD_FDF allows programmers to mark CAN FD frames in the case of + * using struct canfd_frame for mixed CAN / CAN FD content (dual use). + * Since the introduction of CAN XL the CANFD_FDF flag is set in all CAN FD + * frame structures provided by the CAN subsystem of the Linux kernel. + */ +#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */ +#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */ +#define CANFD_FDF 0x04 /* mark CAN FD for dual use of struct canfd_frame */ + +/** + * struct canfd_frame - CAN flexible data rate frame structure + * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition + * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN) + * @flags: additional flags for CAN FD + * @__res0: reserved / padding + * @__res1: reserved / padding + * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte) + */ +struct canfd_frame { + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ + __u8 len; /* frame payload length in byte */ + __u8 flags; /* additional flags for CAN FD */ + __u8 __res0; /* reserved / padding */ + __u8 __res1; /* reserved / padding */ + __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8))); +}; + +/* + * defined bits for canxl_frame.flags + * + * The canxl_frame.flags element contains two bits CANXL_XLF and CANXL_SEC + * and shares the relative position of the struct can[fd]_frame.len element. + * The CANXL_XLF bit ALWAYS needs to be set to indicate a valid CAN XL frame. + * As a side effect setting this bit intentionally breaks the length checks + * for Classical CAN and CAN FD frames. + * + * Undefined bits in canxl_frame.flags are reserved and shall be set to zero. + */ +#define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */ +#define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */ + +/** + * struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure + * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags + * @flags: additional flags for CAN XL + * @sdt: SDU (service data unit) type + * @len: frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN) + * @af: acceptance field + * @data: CAN XL frame payload (CANXL_MIN_DLEN .. CANXL_MAX_DLEN byte) + * + * @prio shares the same position as @can_id from struct can[fd]_frame. + */ +struct canxl_frame { + canid_t prio; /* 11 bit priority for arbitration (canid_t) */ + __u8 flags; /* additional flags for CAN XL */ + __u8 sdt; /* SDU (service data unit) type */ + __u16 len; /* frame payload length in byte */ + __u32 af; /* acceptance field */ + __u8 data[CANXL_MAX_DLEN]; +}; + +#define CAN_MTU (sizeof(struct can_frame)) +#define CANFD_MTU (sizeof(struct canfd_frame)) +#define CANXL_MTU (sizeof(struct canxl_frame)) +#define CANXL_HDR_SIZE (offsetof(struct canxl_frame, data)) +#define CANXL_MIN_MTU (CANXL_HDR_SIZE + 64) +#define CANXL_MAX_MTU CANXL_MTU + +/* particular protocols of the protocol family PF_CAN */ +#define CAN_RAW 1 /* RAW sockets */ +#define CAN_BCM 2 /* Broadcast Manager */ +#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */ +#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */ +#define CAN_MCNET 5 /* Bosch MCNet */ +#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */ +#define CAN_J1939 7 /* SAE J1939 */ +#define CAN_NPROTO 8 + +#define SOL_CAN_BASE 100 + +/* + * This typedef was introduced in Linux v3.1-rc2 + * (commit 6602a4b net: Make userland include of netlink.h more sane) + * in <linux/socket.h>. It must be duplicated here to make the CAN + * headers self-contained. + */ +typedef unsigned short __kernel_sa_family_t; + +/** + * struct sockaddr_can - the sockaddr structure for CAN sockets + * @can_family: address family number AF_CAN. + * @can_ifindex: CAN network interface index. + * @can_addr: protocol specific address information + */ +struct sockaddr_can { + __kernel_sa_family_t can_family; + int can_ifindex; + union { + /* transport protocol class address information (e.g. ISOTP) */ + struct { canid_t rx_id, tx_id; } tp; + + /* J1939 address information */ + struct { + /* 8 byte name when using dynamic addressing */ + __u64 name; + + /* pgn: + * 8 bit: PS in PDU2 case, else 0 + * 8 bit: PF + * 1 bit: DP + * 1 bit: reserved + */ + __u32 pgn; + + /* 1 byte address */ + __u8 addr; + } j1939; + + /* reserved for future CAN protocols address information */ + } can_addr; +}; + +/** + * struct can_filter - CAN ID based filter in can_register(). + * @can_id: relevant bits of CAN ID which are not masked out. + * @can_mask: CAN mask (see description) + * + * Description: + * A filter matches, when + * + * <received_can_id> & mask == can_id & mask + * + * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can + * filter for error message frames (CAN_ERR_FLAG bit set in mask). + */ +struct can_filter { + canid_t can_id; + canid_t can_mask; +}; + +#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ +#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */ + +#endif /* !_UAPI_CAN_H */ diff --git a/libmnl/include/linux/can/Makefile.am b/libmnl/include/linux/can/Makefile.am new file mode 100644 index 0000000..2d02887 --- /dev/null +++ b/libmnl/include/linux/can/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = netlink.h diff --git a/libmnl/include/linux/can/netlink.h b/libmnl/include/linux/can/netlink.h new file mode 100644 index 0000000..02ec32d --- /dev/null +++ b/libmnl/include/linux/can/netlink.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * linux/can/netlink.h + * + * Definitions for the CAN netlink interface + * + * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _UAPI_CAN_NETLINK_H +#define _UAPI_CAN_NETLINK_H + +#include <linux/types.h> + +/* + * CAN bit-timing parameters + * + * For further information, please read chapter "8 BIT TIMING + * REQUIREMENTS" of the "Bosch CAN Specification version 2.0" + * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf. + */ +struct can_bittiming { + __u32 bitrate; /* Bit-rate in bits/second */ + __u32 sample_point; /* Sample point in one-tenth of a percent */ + __u32 tq; /* Time quanta (TQ) in nanoseconds */ + __u32 prop_seg; /* Propagation segment in TQs */ + __u32 phase_seg1; /* Phase buffer segment 1 in TQs */ + __u32 phase_seg2; /* Phase buffer segment 2 in TQs */ + __u32 sjw; /* Synchronisation jump width in TQs */ + __u32 brp; /* Bit-rate prescaler */ +}; + +/* + * CAN hardware-dependent bit-timing constant + * + * Used for calculating and checking bit-timing parameters + */ +struct can_bittiming_const { + char name[16]; /* Name of the CAN controller hardware */ + __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */ + __u32 tseg1_max; + __u32 tseg2_min; /* Time segment 2 = phase_seg2 */ + __u32 tseg2_max; + __u32 sjw_max; /* Synchronisation jump width */ + __u32 brp_min; /* Bit-rate prescaler */ + __u32 brp_max; + __u32 brp_inc; +}; + +/* + * CAN clock parameters + */ +struct can_clock { + __u32 freq; /* CAN system clock frequency in Hz */ +}; + +/* + * CAN operational and error states + */ +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +/* + * CAN bus error counters + */ +struct can_berr_counter { + __u16 txerr; + __u16 rxerr; +}; + +/* + * CAN controller mode + */ +struct can_ctrlmode { + __u32 mask; + __u32 flags; +}; + +#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */ +#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ +#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */ +#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ +#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ +#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */ +#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */ +#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */ +#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */ +#define CAN_CTRLMODE_TDC_AUTO 0x200 /* CAN transiver automatically calculates TDCV */ +#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* TDCV is manually set up by user */ + +/* + * CAN device statistics + */ +struct can_device_stats { + __u32 bus_error; /* Bus errors */ + __u32 error_warning; /* Changes to error warning state */ + __u32 error_passive; /* Changes to error passive state */ + __u32 bus_off; /* Changes to bus off state */ + __u32 arbitration_lost; /* Arbitration lost errors */ + __u32 restarts; /* CAN controller re-starts */ +}; + +/* + * CAN netlink interface + */ +enum { + IFLA_CAN_UNSPEC, + IFLA_CAN_BITTIMING, + IFLA_CAN_BITTIMING_CONST, + IFLA_CAN_CLOCK, + IFLA_CAN_STATE, + IFLA_CAN_CTRLMODE, + IFLA_CAN_RESTART_MS, + IFLA_CAN_RESTART, + IFLA_CAN_BERR_COUNTER, + IFLA_CAN_DATA_BITTIMING, + IFLA_CAN_DATA_BITTIMING_CONST, + IFLA_CAN_TERMINATION, + IFLA_CAN_TERMINATION_CONST, + IFLA_CAN_BITRATE_CONST, + IFLA_CAN_DATA_BITRATE_CONST, + IFLA_CAN_BITRATE_MAX, + IFLA_CAN_TDC, + IFLA_CAN_CTRLMODE_EXT, + + /* add new constants above here */ + __IFLA_CAN_MAX, + IFLA_CAN_MAX = __IFLA_CAN_MAX - 1 +}; + +/* + * CAN FD Transmitter Delay Compensation (TDC) + * + * Please refer to struct can_tdc_const and can_tdc in + * include/linux/can/bittiming.h for further details. + */ +enum { + IFLA_CAN_TDC_UNSPEC, + IFLA_CAN_TDC_TDCV_MIN, /* u32 */ + IFLA_CAN_TDC_TDCV_MAX, /* u32 */ + IFLA_CAN_TDC_TDCO_MIN, /* u32 */ + IFLA_CAN_TDC_TDCO_MAX, /* u32 */ + IFLA_CAN_TDC_TDCF_MIN, /* u32 */ + IFLA_CAN_TDC_TDCF_MAX, /* u32 */ + IFLA_CAN_TDC_TDCV, /* u32 */ + IFLA_CAN_TDC_TDCO, /* u32 */ + IFLA_CAN_TDC_TDCF, /* u32 */ + + /* add new constants above here */ + __IFLA_CAN_TDC, + IFLA_CAN_TDC_MAX = __IFLA_CAN_TDC - 1 +}; + +/* + * IFLA_CAN_CTRLMODE_EXT nest: controller mode extended parameters + */ +enum { + IFLA_CAN_CTRLMODE_UNSPEC, + IFLA_CAN_CTRLMODE_SUPPORTED, /* u32 */ + + /* add new constants above here */ + __IFLA_CAN_CTRLMODE, + IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1 +}; + +/* u16 termination range: 1..65535 Ohms */ +#define CAN_TERMINATION_DISABLED 0 + +#endif /* !_UAPI_CAN_NETLINK_H */ diff --git a/libmnl/include/linux/netfilter/Makefile.am b/libmnl/include/linux/netfilter/Makefile.am new file mode 100644 index 0000000..64a975e --- /dev/null +++ b/libmnl/include/linux/netfilter/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = nfnetlink_conntrack.h diff --git a/libmnl/include/linux/netfilter/nfnetlink_conntrack.h b/libmnl/include/linux/netfilter/nfnetlink_conntrack.h new file mode 100644 index 0000000..08fabc6 --- /dev/null +++ b/libmnl/include/linux/netfilter/nfnetlink_conntrack.h @@ -0,0 +1,252 @@ +#ifndef _IPCONNTRACK_NETLINK_H +#define _IPCONNTRACK_NETLINK_H +#include <linux/netfilter/nfnetlink.h> + +enum cntl_msg_types { + IPCTNL_MSG_CT_NEW, + IPCTNL_MSG_CT_GET, + IPCTNL_MSG_CT_DELETE, + IPCTNL_MSG_CT_GET_CTRZERO, + IPCTNL_MSG_CT_GET_STATS_CPU, + IPCTNL_MSG_CT_GET_STATS, + IPCTNL_MSG_CT_GET_DYING, + IPCTNL_MSG_CT_GET_UNCONFIRMED, + + IPCTNL_MSG_MAX +}; + +enum ctnl_exp_msg_types { + IPCTNL_MSG_EXP_NEW, + IPCTNL_MSG_EXP_GET, + IPCTNL_MSG_EXP_DELETE, + IPCTNL_MSG_EXP_GET_STATS_CPU, + + IPCTNL_MSG_EXP_MAX +}; + + +enum ctattr_type { + CTA_UNSPEC, + CTA_TUPLE_ORIG, + CTA_TUPLE_REPLY, + CTA_STATUS, + CTA_PROTOINFO, + CTA_HELP, + CTA_NAT_SRC, +#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ + CTA_TIMEOUT, + CTA_MARK, + CTA_COUNTERS_ORIG, + CTA_COUNTERS_REPLY, + CTA_USE, + CTA_ID, + CTA_NAT_DST, + CTA_TUPLE_MASTER, + CTA_NAT_SEQ_ADJ_ORIG, + CTA_NAT_SEQ_ADJ_REPLY, + CTA_SECMARK, /* obsolete */ + CTA_ZONE, + CTA_SECCTX, + CTA_TIMESTAMP, + CTA_MARK_MASK, + CTA_LABELS, + CTA_LABELS_MASK, + __CTA_MAX +}; +#define CTA_MAX (__CTA_MAX - 1) + +enum ctattr_tuple { + CTA_TUPLE_UNSPEC, + CTA_TUPLE_IP, + CTA_TUPLE_PROTO, + __CTA_TUPLE_MAX +}; +#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1) + +enum ctattr_ip { + CTA_IP_UNSPEC, + CTA_IP_V4_SRC, + CTA_IP_V4_DST, + CTA_IP_V6_SRC, + CTA_IP_V6_DST, + __CTA_IP_MAX +}; +#define CTA_IP_MAX (__CTA_IP_MAX - 1) + +enum ctattr_l4proto { + CTA_PROTO_UNSPEC, + CTA_PROTO_NUM, + CTA_PROTO_SRC_PORT, + CTA_PROTO_DST_PORT, + CTA_PROTO_ICMP_ID, + CTA_PROTO_ICMP_TYPE, + CTA_PROTO_ICMP_CODE, + CTA_PROTO_ICMPV6_ID, + CTA_PROTO_ICMPV6_TYPE, + CTA_PROTO_ICMPV6_CODE, + __CTA_PROTO_MAX +}; +#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1) + +enum ctattr_protoinfo { + CTA_PROTOINFO_UNSPEC, + CTA_PROTOINFO_TCP, + CTA_PROTOINFO_DCCP, + CTA_PROTOINFO_SCTP, + __CTA_PROTOINFO_MAX +}; +#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) + +enum ctattr_protoinfo_tcp { + CTA_PROTOINFO_TCP_UNSPEC, + CTA_PROTOINFO_TCP_STATE, + CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, + CTA_PROTOINFO_TCP_WSCALE_REPLY, + CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, + CTA_PROTOINFO_TCP_FLAGS_REPLY, + __CTA_PROTOINFO_TCP_MAX +}; +#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) + +enum ctattr_protoinfo_dccp { + CTA_PROTOINFO_DCCP_UNSPEC, + CTA_PROTOINFO_DCCP_STATE, + CTA_PROTOINFO_DCCP_ROLE, + CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ, + __CTA_PROTOINFO_DCCP_MAX, +}; +#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1) + +enum ctattr_protoinfo_sctp { + CTA_PROTOINFO_SCTP_UNSPEC, + CTA_PROTOINFO_SCTP_STATE, + CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + CTA_PROTOINFO_SCTP_VTAG_REPLY, + __CTA_PROTOINFO_SCTP_MAX +}; +#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_MAX - 1) + +enum ctattr_counters { + CTA_COUNTERS_UNSPEC, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ + __CTA_COUNTERS_MAX +}; +#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) + +enum ctattr_tstamp { + CTA_TIMESTAMP_UNSPEC, + CTA_TIMESTAMP_START, + CTA_TIMESTAMP_STOP, + __CTA_TIMESTAMP_MAX +}; +#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1) + +enum ctattr_nat { + CTA_NAT_UNSPEC, + CTA_NAT_V4_MINIP, +#define CTA_NAT_MINIP CTA_NAT_V4_MINIP + CTA_NAT_V4_MAXIP, +#define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP + CTA_NAT_PROTO, + CTA_NAT_V6_MINIP, + CTA_NAT_V6_MAXIP, + __CTA_NAT_MAX +}; +#define CTA_NAT_MAX (__CTA_NAT_MAX - 1) + +enum ctattr_protonat { + CTA_PROTONAT_UNSPEC, + CTA_PROTONAT_PORT_MIN, + CTA_PROTONAT_PORT_MAX, + __CTA_PROTONAT_MAX +}; +#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) + +enum ctattr_natseq { + CTA_NAT_SEQ_UNSPEC, + CTA_NAT_SEQ_CORRECTION_POS, + CTA_NAT_SEQ_OFFSET_BEFORE, + CTA_NAT_SEQ_OFFSET_AFTER, + __CTA_NAT_SEQ_MAX +}; +#define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1) + +enum ctattr_expect { + CTA_EXPECT_UNSPEC, + CTA_EXPECT_MASTER, + CTA_EXPECT_TUPLE, + CTA_EXPECT_MASK, + CTA_EXPECT_TIMEOUT, + CTA_EXPECT_ID, + CTA_EXPECT_HELP_NAME, + CTA_EXPECT_ZONE, + CTA_EXPECT_FLAGS, + CTA_EXPECT_CLASS, + CTA_EXPECT_NAT, + CTA_EXPECT_FN, + __CTA_EXPECT_MAX +}; +#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1) + +enum ctattr_expect_nat { + CTA_EXPECT_NAT_UNSPEC, + CTA_EXPECT_NAT_DIR, + CTA_EXPECT_NAT_TUPLE, + __CTA_EXPECT_NAT_MAX +}; +#define CTA_EXPECT_NAT_MAX (__CTA_EXPECT_NAT_MAX - 1) + +enum ctattr_help { + CTA_HELP_UNSPEC, + CTA_HELP_NAME, + CTA_HELP_INFO, + __CTA_HELP_MAX +}; +#define CTA_HELP_MAX (__CTA_HELP_MAX - 1) + +enum ctattr_secctx { + CTA_SECCTX_UNSPEC, + CTA_SECCTX_NAME, + __CTA_SECCTX_MAX +}; +#define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1) + +enum ctattr_stats_cpu { + CTA_STATS_UNSPEC, + CTA_STATS_SEARCHED, + CTA_STATS_FOUND, + CTA_STATS_NEW, + CTA_STATS_INVALID, + CTA_STATS_IGNORE, + CTA_STATS_DELETE, + CTA_STATS_DELETE_LIST, + CTA_STATS_INSERT, + CTA_STATS_INSERT_FAILED, + CTA_STATS_DROP, + CTA_STATS_EARLY_DROP, + CTA_STATS_ERROR, + CTA_STATS_SEARCH_RESTART, + __CTA_STATS_MAX, +}; +#define CTA_STATS_MAX (__CTA_STATS_MAX - 1) + +enum ctattr_stats_global { + CTA_STATS_GLOBAL_UNSPEC, + CTA_STATS_GLOBAL_ENTRIES, + __CTA_STATS_GLOBAL_MAX, +}; +#define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1) + +enum ctattr_expect_stats { + CTA_STATS_EXP_UNSPEC, + CTA_STATS_EXP_NEW, + CTA_STATS_EXP_CREATE, + CTA_STATS_EXP_DELETE, + __CTA_STATS_EXP_MAX, +}; +#define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1) + +#endif /* _IPCONNTRACK_NETLINK_H */ diff --git a/libmnl/include/linux/netlink.h b/libmnl/include/linux/netlink.h new file mode 100644 index 0000000..ced0e1a --- /dev/null +++ b/libmnl/include/linux/netlink.h @@ -0,0 +1,153 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include <linux/socket.h> /* for __kernel_sa_family_t */ +#include <linux/types.h> + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ +#define NETLINK_SOCK_DIAG 4 /* socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_GENERIC 16 +/* leave room for NETLINK_DM (DM Events) */ +#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ +#define NETLINK_ECRYPTFS 19 +#define NETLINK_RDMA 20 +#define NETLINK_CRYPTO 21 /* Crypto layer */ + +#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG + +#define MAX_LINKS 32 + +struct sockaddr_nl { + __kernel_sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* port ID */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr { + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process port ID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 1 /* It is request message. */ +#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 8 /* Echo this request */ +#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4U +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ + +struct nlmsgerr { + int error; + struct nlmsghdr msg; +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 + +struct nl_pktinfo { + __u32 group; +}; + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr { + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + + +#endif /* __LINUX_NETLINK_H */ diff --git a/libmnl/include/linux/socket.h b/libmnl/include/linux/socket.h new file mode 100644 index 0000000..8c1e501 --- /dev/null +++ b/libmnl/include/linux/socket.h @@ -0,0 +1,21 @@ +#ifndef _LINUX_SOCKET_H +#define _LINUX_SOCKET_H + +/* + * Desired design of maximum size and alignment (see RFC2553) + */ +#define _K_SS_MAXSIZE 128 /* Implementation specific max size */ +#define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *)) + /* Implementation specific desired alignment */ + +typedef unsigned short __kernel_sa_family_t; + +struct __kernel_sockaddr_storage { + __kernel_sa_family_t ss_family; /* address family */ + /* Following field(s) are implementation specific */ + char __data[_K_SS_MAXSIZE - sizeof(unsigned short)]; + /* space to achieve desired size, */ + /* _SS_MAXSIZE value minus size of ss_family */ +} __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */ + +#endif /* _LINUX_SOCKET_H */ diff --git a/libmnl/libmnl.pc.in b/libmnl/libmnl.pc.in new file mode 100644 index 0000000..8a24315 --- /dev/null +++ b/libmnl/libmnl.pc.in @@ -0,0 +1,15 @@ +# libmnl pkg-config file + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libmnl +Description: Minimalistic Netlink communication library +URL: http://netfilter.org/projects/libmnl/ +Version: @VERSION@ +Requires: +Conflicts: +Libs: -L${libdir} -lmnl +Cflags: -I${includedir} diff --git a/libmnl/m4/.gitignore b/libmnl/m4/.gitignore new file mode 100644 index 0000000..64d9bbc --- /dev/null +++ b/libmnl/m4/.gitignore @@ -0,0 +1,2 @@ +/libtool.m4 +/lt*.m4 diff --git a/libmnl/m4/gcc4_visibility.m4 b/libmnl/m4/gcc4_visibility.m4 new file mode 100644 index 0000000..214d3f3 --- /dev/null +++ b/libmnl/m4/gcc4_visibility.m4 @@ -0,0 +1,21 @@ + +# GCC 4.x -fvisibility=hidden + +AC_DEFUN([CHECK_GCC_FVISIBILITY], [ + AC_LANG_PUSH([C]) + saved_CFLAGS="$CFLAGS" + CFLAGS="$saved_CFLAGS -fvisibility=hidden" + AC_CACHE_CHECK([whether compiler accepts -fvisibility=hidden], + [ac_cv_fvisibility_hidden], AC_COMPILE_IFELSE( + [AC_LANG_SOURCE()], + [ac_cv_fvisibility_hidden=yes], + [ac_cv_fvisibility_hidden=no] + )) + if test "$ac_cv_fvisibility_hidden" = "yes"; then + AC_DEFINE([HAVE_VISIBILITY_HIDDEN], [1], + [True if compiler supports -fvisibility=hidden]) + AC_SUBST([GCC_FVISIBILITY_HIDDEN], [-fvisibility=hidden]) + fi + CFLAGS="$saved_CFLAGS" + AC_LANG_POP([C]) +]) diff --git a/libmnl/src/Makefile.am b/libmnl/src/Makefile.am new file mode 100644 index 0000000..9aab516 --- /dev/null +++ b/libmnl/src/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/Make_global.am +lib_LTLIBRARIES = libmnl.la + +libmnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libmnl.map -version-info $(LIBVERSION) +libmnl_la_SOURCES = socket.c callback.c nlmsg.c attr.c internal.h libmnl.map diff --git a/libmnl/src/attr.c b/libmnl/src/attr.c new file mode 100644 index 0000000..bc39df4 --- /dev/null +++ b/libmnl/src/attr.c @@ -0,0 +1,742 @@ +/* + * (C) 2008-2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ +#include <limits.h> /* for INT_MAX */ +#include <libmnl/libmnl.h> +#include <string.h> +#include <errno.h> +#include "internal.h" + +/** + * \defgroup attr Netlink attribute helpers + * + * Netlink Type-Length-Value (TLV) attribute: + * \verbatim + |<-- 2 bytes -->|<-- 2 bytes -->|<-- variable -->| + ------------------------------------------------- + | length | type | value | + ------------------------------------------------- + |<--------- header ------------>|<-- payload --->| +\endverbatim + * The payload of the Netlink message contains sequences of attributes that are + * expressed in TLV format. + * + * @{ + */ + +/** + * mnl_attr_get_type - get type of netlink attribute + * \param attr pointer to netlink attribute + * + * \return the attribute type + */ +EXPORT_SYMBOL uint16_t mnl_attr_get_type(const struct nlattr *attr) +{ + return attr->nla_type & NLA_TYPE_MASK; +} + +/** + * mnl_attr_get_len - get length of netlink attribute + * \param attr pointer to netlink attribute + * + * \return the attribute length + * + * The attribute length is the length of the attribute header plus the + * attribute payload. + * + */ +EXPORT_SYMBOL uint16_t mnl_attr_get_len(const struct nlattr *attr) +{ + return attr->nla_len; +} + +/** + * mnl_attr_get_payload_len - get the attribute payload-value length + * \param attr pointer to netlink attribute + * + * \return the attribute payload-value length + */ +EXPORT_SYMBOL uint16_t mnl_attr_get_payload_len(const struct nlattr *attr) +{ + return attr->nla_len - MNL_ATTR_HDRLEN; +} + +/** + * mnl_attr_get_payload - get pointer to the attribute payload + * \param attr pointer to netlink attribute + * + * \return pointer to the attribute payload + */ +EXPORT_SYMBOL void *mnl_attr_get_payload(const struct nlattr *attr) +{ + return (void *)attr + MNL_ATTR_HDRLEN; +} + +/** + * mnl_attr_ok - check if there is room for an attribute in a buffer + * \param attr attribute that we want to check if there is room for + * \param len remaining bytes in a buffer that contains the attribute + * + * This function is used to check that a buffer, which is supposed to contain + * an attribute, has enough room for the attribute that it stores, i.e. this + * function can be used to verify that an attribute is neither malformed nor + * truncated. + * + * This function does not set errno in case of error since it is intended + * for iterations. + * + * The len parameter may be negative in the case of malformed messages during + * attribute iteration, that is why we use a signed integer. + * + * \return true if there is room for the attribute, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_ok(const struct nlattr *attr, int len) +{ + return len >= (int)sizeof(struct nlattr) && + attr->nla_len >= sizeof(struct nlattr) && + (int)attr->nla_len <= len; +} + +/** + * mnl_attr_next - get the next attribute in the payload of a netlink message + * \param attr pointer to the current attribute + * + * \return a pointer to the next attribute after the one passed in + * + * You have to use mnl_attr_ok() on the returned attribute to ensure that the + * next attribute is valid. + * + */ +EXPORT_SYMBOL struct nlattr *mnl_attr_next(const struct nlattr *attr) +{ + return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len)); +} + +/** + * mnl_attr_type_valid - check if the attribute type is valid + * \param attr pointer to attribute to be checked + * \param max maximum attribute type + * + * This function allows one to check if the attribute type is higher than the + * maximum supported type. + * + * Strict attribute checking in user-space is not a good idea since you may + * run an old application with a newer kernel that supports new attributes. + * This leads to backward compatibility breakages in user-space. Better check + * if you support an attribute, if not, skip it. + * + * On an error, errno is explicitly set. + * + * \return 1 if the attribute is valid, -1 otherwise + * + */ +EXPORT_SYMBOL int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max) +{ + if (mnl_attr_get_type(attr) > max) { + errno = EOPNOTSUPP; + return -1; + } + return 1; +} + +static int __mnl_attr_validate(const struct nlattr *attr, + enum mnl_attr_data_type type, size_t exp_len) +{ + uint16_t attr_len = mnl_attr_get_payload_len(attr); + const char *attr_data = mnl_attr_get_payload(attr); + + if (attr_len < exp_len) { + errno = ERANGE; + return -1; + } + switch(type) { + case MNL_TYPE_FLAG: + if (attr_len > 0) { + errno = ERANGE; + return -1; + } + break; + case MNL_TYPE_NUL_STRING: + if (attr_len == 0) { + errno = ERANGE; + return -1; + } + if (attr_data[attr_len-1] != '\0') { + errno = EINVAL; + return -1; + } + break; + case MNL_TYPE_STRING: + if (attr_len == 0) { + errno = ERANGE; + return -1; + } + break; + case MNL_TYPE_NESTED: + /* empty nested attributes are OK. */ + if (attr_len == 0) + break; + /* if not empty, they must contain one header, eg. flag */ + if (attr_len < MNL_ATTR_HDRLEN) { + errno = ERANGE; + return -1; + } + break; + default: + /* make gcc happy. */ + break; + } + if (exp_len && attr_len > exp_len) { + errno = ERANGE; + return -1; + } + return 0; +} + +static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = { + [MNL_TYPE_U8] = sizeof(uint8_t), + [MNL_TYPE_U16] = sizeof(uint16_t), + [MNL_TYPE_U32] = sizeof(uint32_t), + [MNL_TYPE_U64] = sizeof(uint64_t), + [MNL_TYPE_MSECS] = sizeof(uint64_t), +}; + +/** + * mnl_attr_validate - validate netlink attribute (simplified version) + * \param attr pointer to netlink attribute that we want to validate + * \param type data type (see enum mnl_attr_data_type) + * + * The validation is based on the data type. Specifically, it checks that + * integers (u8, u16, u32 and u64) have enough room for them. + * + * On an error, errno is explicitly set. + * + * \return 0 on success, -1 on error + */ +EXPORT_SYMBOL int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type) +{ + int exp_len; + + if (type >= MNL_TYPE_MAX) { + errno = EINVAL; + return -1; + } + exp_len = mnl_attr_data_type_len[type]; + return __mnl_attr_validate(attr, type, exp_len); +} + +/** + * mnl_attr_validate2 - validate netlink attribute (extended version) + * \param attr pointer to netlink attribute that we want to validate + * \param type attribute type (see enum mnl_attr_data_type) + * \param exp_len expected attribute data size + * + * This function allows one to perform a more accurate validation for attributes + * whose size is variable. + * + * On an error, errno is explicitly set. + * + * \return 0 if the attribute is valid and fits within the expected length, -1 + * otherwise + */ +EXPORT_SYMBOL int mnl_attr_validate2(const struct nlattr *attr, + enum mnl_attr_data_type type, + size_t exp_len) +{ + if (type >= MNL_TYPE_MAX) { + errno = EINVAL; + return -1; + } + return __mnl_attr_validate(attr, type, exp_len); +} + +/** + * mnl_attr_parse - parse attributes + * \param nlh pointer to netlink message + * \param offset offset to start parsing from (if payload is after any header) + * \param cb callback function that is called for each attribute + * \param data pointer to data that is passed to the callback function + * + * This function allows you to iterate over the sequence of attributes that + * compose the Netlink message. You can then put the attribute in an array as it + * usually happens at this stage or you can use any other data structure (such + * as lists or trees). + * + * \return propagated value from callback, one of MNL_CB_ERROR, MNL_CB_STOP + * or MNL_CB_OK + */ +EXPORT_SYMBOL int mnl_attr_parse(const struct nlmsghdr *nlh, + unsigned int offset, mnl_attr_cb_t cb, + void *data) +{ + int ret = MNL_CB_OK; + const struct nlattr *attr; + + mnl_attr_for_each(attr, nlh, offset) + if ((ret = cb(attr, data)) <= MNL_CB_STOP) + return ret; + return ret; +} + +/** + * mnl_attr_parse_nested - parse attributes inside a nest + * \param nested pointer to netlink attribute that contains a nest + * \param cb callback function that is called for each attribute in the nest + * \param data pointer to data passed to the callback function + * + * This function allows you to iterate over the sequence of attributes that + * compose the Netlink message. You can then put the attribute in an array as it + * usually happens at this stage or you can use any other data structure (such + * as lists or trees). + * +* \return propagated value from callback, one of MNL_CB_ERROR, MNL_CB_STOP +* or MNL_CB_OK + */ +EXPORT_SYMBOL int mnl_attr_parse_nested(const struct nlattr *nested, + mnl_attr_cb_t cb, void *data) +{ + int ret = MNL_CB_OK; + const struct nlattr *attr; + + mnl_attr_for_each_nested(attr, nested) + if ((ret = cb(attr, data)) <= MNL_CB_STOP) + return ret; + return ret; +} + +/** + * mnl_attr_parse_payload - parse attributes in payload of Netlink message + * \param payload pointer to payload of the Netlink message + * \param payload_len payload length that contains the attributes + * \param cb callback function that is called for each attribute + * \param data pointer to data that is passed to the callback function + * + * This function takes a pointer to the area that contains the attributes, + * commonly known as the payload of the Netlink message. Thus, you have to + * pass a pointer to the Netlink message payload, instead of the entire + * message. + * + * This function allows you to iterate over the sequence of attributes that are + * located at some payload offset. You can then put the attributes in one array + * as usual, or you can use any other data structure (such as lists or trees). + * + * \return propagated value from callback, one of MNL_CB_ERROR, MNL_CB_STOP + * or MNL_CB_OK + */ +EXPORT_SYMBOL int mnl_attr_parse_payload(const void *payload, + size_t payload_len, + mnl_attr_cb_t cb, void *data) +{ + int ret = MNL_CB_OK; + const struct nlattr *attr; + + mnl_attr_for_each_payload(payload, payload_len) + if ((ret = cb(attr, data)) <= MNL_CB_STOP) + return ret; + return ret; +} + +/** + * mnl_attr_get_u8 - get 8-bit unsigned integer attribute payload + * \param attr pointer to netlink attribute + * + * \return 8-bit value of the attribute payload + */ +EXPORT_SYMBOL uint8_t mnl_attr_get_u8(const struct nlattr *attr) +{ + return *((uint8_t *)mnl_attr_get_payload(attr)); +} + +/** + * mnl_attr_get_u16 - get 16-bit unsigned integer attribute payload + * \param attr pointer to netlink attribute + * + * \return 16-bit value of the attribute payload + */ +EXPORT_SYMBOL uint16_t mnl_attr_get_u16(const struct nlattr *attr) +{ + return *((uint16_t *)mnl_attr_get_payload(attr)); +} + +/** + * mnl_attr_get_u32 - get 32-bit unsigned integer attribute payload + * \param attr pointer to netlink attribute + * + * \return 32-bit value of the attribute payload + */ +EXPORT_SYMBOL uint32_t mnl_attr_get_u32(const struct nlattr *attr) +{ + return *((uint32_t *)mnl_attr_get_payload(attr)); +} + +/** + * mnl_attr_get_u64 - get 64-bit unsigned integer attribute + * \param attr pointer to netlink attribute + * + * This function reads the 64-bit nlattr payload in an alignment safe manner. + * + * \return 64-bit value of the attribute payload + */ +EXPORT_SYMBOL uint64_t mnl_attr_get_u64(const struct nlattr *attr) +{ + uint64_t tmp; + memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp)); + return tmp; +} + +/** + * mnl_attr_get_str - get pointer to string attribute + * \param attr pointer to netlink attribute + * + * \return string pointer of the attribute payload + */ +EXPORT_SYMBOL const char *mnl_attr_get_str(const struct nlattr *attr) +{ + return mnl_attr_get_payload(attr); +} + +/** + * mnl_attr_put - add an attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type that you want to add + * \param len netlink attribute payload length + * \param data pointer to the data that will be stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +EXPORT_SYMBOL void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, + size_t len, const void *data) +{ + struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh); + uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len; + int pad; + + attr->nla_type = type; + attr->nla_len = payload_len; + memcpy(mnl_attr_get_payload(attr), data, len); + pad = MNL_ALIGN(len) - len; + if (pad > 0) + memset(mnl_attr_get_payload(attr) + len, 0, pad); + + nlh->nlmsg_len += MNL_ALIGN(payload_len); +} + +/** + * mnl_attr_put_u8 - add 8-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 8-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +EXPORT_SYMBOL void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, + uint8_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint8_t), &data); +} + +/** + * mnl_attr_put_u16 - add 16-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 16-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +EXPORT_SYMBOL void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, + uint16_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint16_t), &data); +} + +/** + * mnl_attr_put_u32 - add 32-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 32-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +EXPORT_SYMBOL void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, + uint32_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint32_t), &data); +} + +/** + * mnl_attr_put_u64 - add 64-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 64-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +EXPORT_SYMBOL void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, + uint64_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint64_t), &data); +} + +/** + * mnl_attr_put_str - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +EXPORT_SYMBOL void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, + const char *data) +{ + mnl_attr_put(nlh, type, strlen(data), data); +} + +/** + * mnl_attr_put_strz - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function is similar to mnl_attr_put_str, but it includes the + * NUL/zero ('\0') terminator at the end of the string. + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +EXPORT_SYMBOL void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, + const char *data) +{ + mnl_attr_put(nlh, type, strlen(data)+1, data); +} + +/** + * mnl_attr_nest_start - start an attribute nest + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * + * This function adds the attribute header that identifies the beginning of + * an attribute nest. + * + * \return valid pointer to the beginning of the nest + */ +EXPORT_SYMBOL struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, + uint16_t type) +{ + struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh); + + /* set start->nla_len in mnl_attr_nest_end() */ + start->nla_type = NLA_F_NESTED | type; + nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr)); + + return start; +} + +/** + * mnl_attr_put_check - add an attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type that you want to add + * \param len netlink attribute payload length + * \param data pointer to the data that will be stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. + * + * \return true if the attribute could be added, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, size_t len, + const void *data) +{ + if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen) + return false; + mnl_attr_put(nlh, type, len, data); + return true; +} + +/** + * mnl_attr_put_u8_check - add 8-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 8-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. + * + * \return true if the attribute could be added, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint8_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data); +} + +/** + * mnl_attr_put_u16_check - add 16-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 16-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. + * + * \return true if the attribute could be added, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint16_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data); +} + +/** + * mnl_attr_put_u32_check - add 32-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 32-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. + * + * \return true if the attribute could be added, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint32_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data); +} + +/** + * mnl_attr_put_u64_check - add 64-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 64-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. + * + * \return true if the attribute could be added, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint64_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint64_t), &data); +} + +/** + * mnl_attr_put_str_check - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. + * + * \return true if the attribute could be added, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, const char *data) +{ + return mnl_attr_put_check(nlh, buflen, type, strlen(data), data); +} + +/** + * mnl_attr_put_strz_check - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function is similar to mnl_attr_put_str, but it includes the + * NUL/zero ('\0') terminator at the end of the string. + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. + * + * \return true if the attribute could be added, false otherwise + */ +EXPORT_SYMBOL bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, const char *data) +{ + return mnl_attr_put_check(nlh, buflen, type, strlen(data)+1, data); +} + +/** + * mnl_attr_nest_start_check - start an attribute nest + * \param buflen size of buffer which stores the message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * + * This function adds the attribute header that identifies the beginning of + * an attribute nest. + * + * \return NULL if the attribute cannot be added, otherwise a pointer to the + * beginning of the nest + */ +EXPORT_SYMBOL struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, + size_t buflen, + uint16_t type) +{ + if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen) + return NULL; + return mnl_attr_nest_start(nlh, type); +} + +/** + * mnl_attr_nest_end - end an attribute nest + * \param nlh pointer to the netlink message + * \param start pointer to the attribute nest returned by mnl_attr_nest_start() + * + * This function updates the attribute header that identifies the nest. + */ +EXPORT_SYMBOL void mnl_attr_nest_end(struct nlmsghdr *nlh, + struct nlattr *start) +{ + start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start; +} + +/** + * mnl_attr_nest_cancel - cancel an attribute nest + * \param nlh pointer to the netlink message + * \param start pointer to the attribute nest returned by mnl_attr_nest_start() + * + * This function updates the attribute header that identifies the nest. + */ +EXPORT_SYMBOL void mnl_attr_nest_cancel(struct nlmsghdr *nlh, + struct nlattr *start) +{ + nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start; +} + +/** + * @} + */ diff --git a/libmnl/src/callback.c b/libmnl/src/callback.c new file mode 100644 index 0000000..f5349c3 --- /dev/null +++ b/libmnl/src/callback.c @@ -0,0 +1,167 @@ +/* + * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include <errno.h> +#include <libmnl/libmnl.h> +#include "internal.h" + +static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +static int mnl_cb_error(const struct nlmsghdr *nlh, void *data) +{ + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + + if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) { + errno = EBADMSG; + return MNL_CB_ERROR; + } + /* Netlink subsystems returns the errno value with different signess */ + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + + return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR; +} + +static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_STOP; +} + +static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = { + [NLMSG_NOOP] = mnl_cb_noop, + [NLMSG_ERROR] = mnl_cb_error, + [NLMSG_DONE] = mnl_cb_stop, + [NLMSG_OVERRUN] = mnl_cb_noop, +}; + +static inline int __mnl_cb_run(const void *buf, size_t numbytes, + unsigned int seq, unsigned int portid, + mnl_cb_t cb_data, void *data, + const mnl_cb_t *cb_ctl_array, + unsigned int cb_ctl_array_len) +{ + int ret = MNL_CB_OK, len = numbytes; + const struct nlmsghdr *nlh = buf; + + while (mnl_nlmsg_ok(nlh, len)) { + /* check message source */ + if (!mnl_nlmsg_portid_ok(nlh, portid)) { + errno = ESRCH; + return -1; + } + /* perform sequence tracking */ + if (!mnl_nlmsg_seq_ok(nlh, seq)) { + errno = EPROTO; + return -1; + } + + /* dump was interrupted */ + if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) { + errno = EINTR; + return -1; + } + + /* netlink data message handling */ + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + if (cb_data){ + ret = cb_data(nlh, data); + if (ret <= MNL_CB_STOP) + goto out; + } + } else if (nlh->nlmsg_type < cb_ctl_array_len) { + if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) { + ret = cb_ctl_array[nlh->nlmsg_type](nlh, data); + if (ret <= MNL_CB_STOP) + goto out; + } + } else if (default_cb_array[nlh->nlmsg_type]) { + ret = default_cb_array[nlh->nlmsg_type](nlh, data); + if (ret <= MNL_CB_STOP) + goto out; + } + nlh = mnl_nlmsg_next(nlh, &len); + } +out: + return ret; +} + +/** + * \defgroup callback Callback helpers + * @{ + */ + +/** + * mnl_cb_run2 - callback runqueue for netlink messages + * \param buf buffer that contains the netlink messages + * \param numbytes number of bytes stored in the buffer + * \param seq sequence number that we expect to receive + * \param portid Netlink PortID that we expect to receive + * \param cb_data callback handler for data messages + * \param data pointer to data that will be passed to the data callback handler + * \param cb_ctl_array array of custom callback handlers from control messages + * \param cb_ctl_array_len array length of custom control callback handlers + * + * You can set the cb_ctl_array to NULL if you want to use the default control + * callback handlers, in that case, the parameter cb_ctl_array_len is not + * checked. + * + * Your callback may return three possible values: + * - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue. + * - MNL_CB_STOP (=0): stop callback runqueue. + * - MNL_CB_OK (>=1): no problem has occurred. + * + * This function propagates the callback return value. On error, it returns + * -1 and errno is explicitly set. If the portID is not the expected, errno + * is set to ESRCH. If the sequence number is not the expected, errno is set + * to EPROTO. If the dump was interrupted, errno is set to EINTR and you should + * request a new fresh dump again. + */ +EXPORT_SYMBOL int mnl_cb_run2(const void *buf, size_t numbytes, + unsigned int seq, unsigned int portid, + mnl_cb_t cb_data, void *data, + const mnl_cb_t *cb_ctl_array, + unsigned int cb_ctl_array_len) +{ + return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, + cb_ctl_array, cb_ctl_array_len); +} + +/** + * mnl_cb_run - callback runqueue for netlink messages (simplified version) + * \param buf buffer that contains the netlink messages + * \param numbytes number of bytes stored in the buffer + * \param seq sequence number that we expect to receive + * \param portid Netlink PortID that we expect to receive + * \param cb_data callback handler for data messages + * \param data pointer to data that will be passed to the data callback handler + * + * This function is like mnl_cb_run2() but it does not allow you to set + * the control callback handlers. + * + * Your callback may return three possible values: + * - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue. + * - MNL_CB_STOP (=0): stop callback runqueue. + * - MNL_CB_OK (>=1): no problems has occurred. + * + * This function propagates the callback return value. + */ +EXPORT_SYMBOL int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq, + unsigned int portid, mnl_cb_t cb_data, void *data) +{ + return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0); +} + +/** + * @} + */ diff --git a/libmnl/src/internal.h b/libmnl/src/internal.h new file mode 100644 index 0000000..d69eaf3 --- /dev/null +++ b/libmnl/src/internal.h @@ -0,0 +1,11 @@ +#ifndef INTERNAL_H +#define INTERNAL_H 1 + +#include "config.h" +#ifdef HAVE_VISIBILITY_HIDDEN +# define EXPORT_SYMBOL __attribute__((visibility("default"))) +#else +# define EXPORT_SYMBOL +#endif + +#endif diff --git a/libmnl/src/libmnl.map b/libmnl/src/libmnl.map new file mode 100644 index 0000000..e5920e5 --- /dev/null +++ b/libmnl/src/libmnl.map @@ -0,0 +1,79 @@ +LIBMNL_1.0 { +global: + mnl_attr_get_len; + mnl_attr_get_payload; + mnl_attr_get_payload_len; + mnl_attr_get_str; + mnl_attr_get_type; + mnl_attr_get_u16; + mnl_attr_get_u32; + mnl_attr_get_u64; + mnl_attr_get_u8; + mnl_attr_nest_end; + mnl_attr_nest_start; + mnl_attr_nest_start_check; + mnl_attr_nest_cancel; + mnl_attr_next; + mnl_attr_ok; + mnl_attr_parse; + mnl_attr_parse_nested; + mnl_attr_put; + mnl_attr_put_str; + mnl_attr_put_strz; + mnl_attr_put_u16; + mnl_attr_put_u32; + mnl_attr_put_u64; + mnl_attr_put_u8; + mnl_attr_put_check; + mnl_attr_put_str_check; + mnl_attr_put_strz_check; + mnl_attr_put_u16_check; + mnl_attr_put_u32_check; + mnl_attr_put_u64_check; + mnl_attr_put_u8_check; + mnl_attr_type_valid; + mnl_attr_validate; + mnl_attr_validate2; + mnl_cb_run; + mnl_cb_run2; + mnl_nlmsg_fprintf; + mnl_nlmsg_get_payload; + mnl_nlmsg_get_payload_len; + mnl_nlmsg_get_payload_offset; + mnl_nlmsg_get_payload_tail; + mnl_nlmsg_next; + mnl_nlmsg_ok; + mnl_nlmsg_portid_ok; + mnl_nlmsg_put_extra_header; + mnl_nlmsg_put_header; + mnl_nlmsg_seq_ok; + mnl_nlmsg_size; + mnl_nlmsg_batch_start; + mnl_nlmsg_batch_stop; + mnl_nlmsg_batch_next; + mnl_nlmsg_batch_size; + mnl_nlmsg_batch_reset; + mnl_nlmsg_batch_current; + mnl_nlmsg_batch_head; + mnl_nlmsg_batch_is_empty; + mnl_socket_bind; + mnl_socket_close; + mnl_socket_get_fd; + mnl_socket_get_portid; + mnl_socket_getsockopt; + mnl_socket_open; + mnl_socket_recvfrom; + mnl_socket_sendto; + mnl_socket_setsockopt; + +local: *; +}; + +LIBMNL_1.1 { + mnl_attr_parse_payload; +} LIBMNL_1.0; + +LIBMNL_1.2 { + mnl_socket_open2; + mnl_socket_fdopen; +} LIBMNL_1.1; diff --git a/libmnl/src/nlmsg.c b/libmnl/src/nlmsg.c new file mode 100644 index 0000000..c634501 --- /dev/null +++ b/libmnl/src/nlmsg.c @@ -0,0 +1,587 @@ +/* + * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <libmnl/libmnl.h> +#include "internal.h" + +/** + * \defgroup nlmsg Netlink message helpers + * + * Netlink message: + * \verbatim + |<----------------- 4 bytes ------------------->| + |<----- 2 bytes ------>|<------- 2 bytes ------>| + |-----------------------------------------------| + | Message length (including header) | + |-----------------------------------------------| + | Message type | Message flags | + |-----------------------------------------------| + | Message sequence number | + |-----------------------------------------------| + | Netlink PortID | + |-----------------------------------------------| + | | + . Payload . + |_______________________________________________| +\endverbatim + * + * There is usually an extra header after the the Netlink header (at the + * beginning of the payload). This extra header is specific of the Netlink + * subsystem. After this extra header, it comes the sequence of attributes + * that are expressed in Type-Length-Value (TLV) format. + * + * @{ + */ + +/** + * mnl_nlmsg_size - calculate the size of Netlink message (without alignment) + * \param len length of the Netlink payload + * + * This function returns the size of a netlink message (header plus payload) + * without alignment. + */ +EXPORT_SYMBOL size_t mnl_nlmsg_size(size_t len) +{ + return len + MNL_NLMSG_HDRLEN; +} + +/** + * mnl_nlmsg_get_payload_len - get the length of the Netlink payload + * \param nlh pointer to the header of the Netlink message + * + * This function returns the Length of the netlink payload, ie. the length + * of the full message minus the size of the Netlink header. + */ +EXPORT_SYMBOL size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - MNL_NLMSG_HDRLEN; +} + +/** + * mnl_nlmsg_put_header - reserve and prepare room for Netlink header + * \param buf memory already allocated to store the Netlink header + * + * This function sets to zero the room that is required to put the Netlink + * header in the memory buffer passed as parameter. This function also + * initializes the nlmsg_len field to the size of the Netlink header. This + * function returns a pointer to the Netlink header structure. + */ +EXPORT_SYMBOL struct nlmsghdr *mnl_nlmsg_put_header(void *buf) +{ + int len = MNL_ALIGN(sizeof(struct nlmsghdr)); + struct nlmsghdr *nlh = buf; + + memset(buf, 0, len); + nlh->nlmsg_len = len; + return nlh; +} + +/** + * mnl_nlmsg_put_extra_header - reserve and prepare room for an extra header + * \param nlh pointer to Netlink header + * \param size size of the extra header that we want to put + * + * This function sets to zero the room that is required to put the extra + * header after the initial Netlink header. This function also increases + * the nlmsg_len field. You have to invoke mnl_nlmsg_put_header() before + * you call this function. This function returns a pointer to the extra + * header. + */ +EXPORT_SYMBOL void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, + size_t size) +{ + char *ptr = (char *)nlh + nlh->nlmsg_len; + size_t len = MNL_ALIGN(size); + nlh->nlmsg_len += len; + memset(ptr, 0, len); + return ptr; +} + +/** + * mnl_nlmsg_get_payload - get a pointer to the payload of the netlink message + * \param nlh pointer to a netlink header + * + * This function returns a pointer to the payload of the netlink message. + */ +EXPORT_SYMBOL void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh) +{ + return (void *)nlh + MNL_NLMSG_HDRLEN; +} + +/** + * mnl_nlmsg_get_payload_offset - get a pointer to the payload of the message + * \param nlh pointer to a netlink header + * \param offset offset to the payload of the attributes TLV set + * + * This function returns a pointer to the payload of the netlink message plus + * a given offset. + */ +EXPORT_SYMBOL void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, + size_t offset) +{ + return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset); +} + +/** + * mnl_nlmsg_ok - check a there is room for netlink message + * \param nlh netlink message that we want to check + * \param len remaining bytes in a buffer that contains the netlink message + * + * This function is used to check that a buffer that contains a netlink + * message has enough room for the netlink message that it stores, ie. this + * function can be used to verify that a netlink message is not malformed nor + * truncated. + * + * This function does not set errno in case of error since it is intended + * for iterations. Thus, it returns true on success and false on error. + * + * The len parameter may become negative in malformed messages during message + * iteration, that is why we use a signed integer. + */ +EXPORT_SYMBOL bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len) +{ + return len >= (int)sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + (int)nlh->nlmsg_len <= len; +} + +/** + * mnl_nlmsg_next - get the next netlink message in a multipart message + * \param nlh current netlink message that we are handling + * \param len length of the remaining bytes in the buffer (passed by reference). + * + * This function returns a pointer to the next netlink message that is part + * of a multi-part netlink message. Netlink can batch several messages into + * one buffer so that the receiver has to iterate over the whole set of + * Netlink messages. + * + * You have to use mnl_nlmsg_ok() to check if the next Netlink message is + * valid. + */ +EXPORT_SYMBOL struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, + int *len) +{ + *len -= MNL_ALIGN(nlh->nlmsg_len); + return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len)); +} + +/** + * mnl_nlmsg_get_payload_tail - get the ending of the netlink message + * \param nlh pointer to netlink message + * + * This function returns a pointer to the netlink message tail. This is useful + * to build a message since we continue adding attributes at the end of the + * message. + */ +EXPORT_SYMBOL void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh) +{ + return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len); +} + +/** + * mnl_nlmsg_seq_ok - perform sequence tracking + * \param nlh current netlink message that we are handling + * \param seq last sequence number used to send a message + * + * This functions returns true if the sequence tracking is fulfilled, otherwise + * false is returned. We skip the tracking for netlink messages whose sequence + * number is zero since it is usually reserved for event-based kernel + * notifications. On the other hand, if seq is set but the message sequence + * number is not set (i.e. this is an event message coming from kernel-space), + * then we also skip the tracking. This approach is good if we use the same + * socket to send commands to kernel-space (that we want to track) and to + * listen to events (that we do not track). + */ +EXPORT_SYMBOL bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, + unsigned int seq) +{ + return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true; +} + +/** + * mnl_nlmsg_portid_ok - perform portID origin check + * \param nlh current netlink message that we are handling + * \param portid netlink portid that we want to check + * + * This functions returns true if the origin is fulfilled, otherwise + * false is returned. We skip the tracking for netlink message whose portID + * is zero since it is reserved for event-based kernel notifications. On the + * other hand, if portid is set but the message PortID is not (i.e. this + * is an event message coming from kernel-space), then we also skip the + * tracking. This approach is good if we use the same socket to send commands + * to kernel-space (that we want to track) and to listen to events (that we + * do not track). + */ +EXPORT_SYMBOL bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, + unsigned int portid) +{ + return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true; +} + +static void mnl_nlmsg_fprintf_header(FILE *fd, const struct nlmsghdr *nlh) +{ + fprintf(fd, "----------------\t------------------\n"); + fprintf(fd, "| %.010u |\t| message length |\n", nlh->nlmsg_len); + fprintf(fd, "| %.05u | %c%c%c%c |\t| type | flags |\n", + nlh->nlmsg_type, + nlh->nlmsg_flags & NLM_F_REQUEST ? 'R' : '-', + nlh->nlmsg_flags & NLM_F_MULTI ? 'M' : '-', + nlh->nlmsg_flags & NLM_F_ACK ? 'A' : '-', + nlh->nlmsg_flags & NLM_F_ECHO ? 'E' : '-'); + fprintf(fd, "| %.010u |\t| sequence number|\n", nlh->nlmsg_seq); + fprintf(fd, "| %.010u |\t| port ID |\n", nlh->nlmsg_pid); + fprintf(fd, "----------------\t------------------\n"); +} + +static void mnl_fprintf_attr_color(FILE *fd, const struct nlattr *attr) +{ + fprintf(fd, "|%c[%d;%dm" + "%.5u" + "%c[%dm" + "|" + "%c[%d;%dm" + "%c%c" + "%c[%dm" + "|" + "%c[%d;%dm" + "%.5u" + "%c[%dm|\t", + 27, 1, 31, + attr->nla_len, + 27, 0, + 27, 1, 32, + attr->nla_type & NLA_F_NESTED ? 'N' : '-', + attr->nla_type & NLA_F_NET_BYTEORDER ? 'B' : '-', + 27, 0, + 27, 1, 34, + attr->nla_type & NLA_TYPE_MASK, + 27, 0); +} + +static void mnl_fprintf_attr_raw(FILE *fd, const struct nlattr *attr) +{ + fprintf(fd, "|" + "%.5u" + "|" + "%c%c" + "|" + "%.5u" + "|\t", + attr->nla_len, + attr->nla_type & NLA_F_NESTED ? 'N' : '-', + attr->nla_type & NLA_F_NET_BYTEORDER ? 'B' : '-', + attr->nla_type & NLA_TYPE_MASK); +} + +static void mnl_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh, + size_t extra_header_size) +{ + int colorize = 0; + unsigned int i; + int rem = 0; + int fdnum; + + fdnum = fileno(fd); + if (fdnum != -1) + colorize = isatty(fdnum); + + for (i=sizeof(struct nlmsghdr); i<nlh->nlmsg_len; i+=4) { + char *b = (char *) nlh; + struct nlattr *attr = (struct nlattr *) (b+i); + + /* netlink control message. */ + if (nlh->nlmsg_type < NLMSG_MIN_TYPE) { + fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff & b[i], 0xff & b[i+1], + 0xff & b[i+2], 0xff & b[i+3]); + fprintf(fd, "| |\n"); + /* special handling for the extra header. */ + } else if (extra_header_size > 0) { + extra_header_size -= 4; + fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff & b[i], 0xff & b[i+1], + 0xff & b[i+2], 0xff & b[i+3]); + fprintf(fd, "| extra header |\n"); + /* this seems like an attribute header. */ + } else if (rem == 0 && (attr->nla_type & NLA_TYPE_MASK) != 0) { + if (colorize) { + mnl_fprintf_attr_color(fd, attr); + } else { + mnl_fprintf_attr_raw(fd, attr); + } + fprintf(fd, "|len |flags| type|\n"); + + if (!(attr->nla_type & NLA_F_NESTED)) { + rem = NLA_ALIGN(attr->nla_len) - + sizeof(struct nlattr); + } + /* this is the attribute payload. */ + } else if (rem > 0) { + rem -= 4; + fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff & b[i], 0xff & b[i+1], + 0xff & b[i+2], 0xff & b[i+3]); + fprintf(fd, "| data |"); + fprintf(fd, "\t %c %c %c %c\n", + isprint(b[i]) ? b[i] : ' ', + isprint(b[i+1]) ? b[i+1] : ' ', + isprint(b[i+2]) ? b[i+2] : ' ', + isprint(b[i+3]) ? b[i+3] : ' '); + } + } + fprintf(fd, "----------------\t------------------\n"); +} + +/** + * mnl_nlmsg_fprintf - print netlink message to file + * \param fd pointer to file type + * \param data pointer to the buffer that contains messages to be printed + * \param datalen length of data stored in the buffer + * \param extra_header_size size of the extra header (if any) + * + * This function prints the netlink header to a file handle. + * It may be useful for debugging purposes. One example of the output + * is the following: + * + *\verbatim +---------------- ------------------ +| 0000000040 | | message length | +| 00016 | R-A- | | type | flags | +| 1289148991 | | sequence number| +| 0000000000 | | port ID | +---------------- ------------------ +| 00 00 00 00 | | extra header | +| 00 00 00 00 | | extra header | +| 01 00 00 00 | | extra header | +| 01 00 00 00 | | extra header | +|00008|--|00003| |len |flags| type| +| 65 74 68 30 | | data | e t h 0 +---------------- ------------------ +\endverbatim + * + * This example above shows the netlink message that is send to kernel-space + * to set up the link interface eth0. The netlink and attribute header data + * are displayed in base 10 whereas the extra header and the attribute payload + * are expressed in base 16. The possible flags in the netlink header are: + * + * - R, that indicates that NLM_F_REQUEST is set. + * - M, that indicates that NLM_F_MULTI is set. + * - A, that indicates that NLM_F_ACK is set. + * - E, that indicates that NLM_F_ECHO is set. + * + * The lack of one flag is displayed with '-'. On the other hand, the possible + * attribute flags available are: + * + * - N, that indicates that NLA_F_NESTED is set. + * - B, that indicates that NLA_F_NET_BYTEORDER is set. + */ +EXPORT_SYMBOL void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen, + size_t extra_header_size) +{ + const struct nlmsghdr *nlh = data; + int len = datalen; + + while (mnl_nlmsg_ok(nlh, len)) { + mnl_nlmsg_fprintf_header(fd, nlh); + mnl_nlmsg_fprintf_payload(fd, nlh, extra_header_size); + nlh = mnl_nlmsg_next(nlh, &len); + } +} + +/** + * @} + */ + +/** + * \defgroup batch Netlink message batch helpers + * + * This library provides helpers to batch several messages into one single + * datagram. These helpers do not perform strict memory boundary checkings. + * + * The following figure represents a Netlink message batch: + *\verbatim + |<-------------- MNL_SOCKET_BUFFER_SIZE ------------->| + |<-------------------- batch ------------------>| | + |-----------|-----------|-----------|-----------|-----------| + |<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->| + |-----------|-----------|-----------|-----------|-----------| + ^ ^ + | | + message N message N+1 +\endverbatim + * + * To start the batch, you have to call mnl_nlmsg_batch_start() and you can + * use mnl_nlmsg_batch_stop() to release it. + * + * You have to invoke mnl_nlmsg_batch_next() to get room for a new message + * in the batch. If this function returns NULL, it means that the last + * message that was added (message N+1 in the figure above) does not fit the + * batch. Thus, you have to send the batch (which includes until message N) + * and, then, you have to call mnl_nlmsg_batch_reset() to re-initialize + * the batch (this moves message N+1 to the head of the buffer). For that + * reason, the buffer that you have to use to store the batch must be double + * of MNL_SOCKET_BUFFER_SIZE to ensure that the last message (message N+1) + * that did not fit into the batch is written inside valid memory boundaries. + * + * @{ + */ + +struct mnl_nlmsg_batch { + /* the buffer that is used to store the batch. */ + void *buf; + size_t limit; + size_t buflen; + /* the current netlink message in the batch. */ + void *cur; + bool overflow; +}; + +/** + * mnl_nlmsg_batch_start - initialize a batch + * \param buf pointer to the buffer that will store this batch + * \param limit maximum size of the batch (should be MNL_SOCKET_BUFFER_SIZE). + * + * The buffer that you pass must be double of MNL_SOCKET_BUFFER_SIZE. The + * limit must be half of the buffer size, otherwise expect funny memory + * corruptions 8-). + * + * You can allocate the buffer that you use to store the batch in the stack or + * the heap, no restrictions in this regard. This function returns NULL on + * error. + */ +EXPORT_SYMBOL struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, + size_t limit) +{ + struct mnl_nlmsg_batch *b; + + b = malloc(sizeof(struct mnl_nlmsg_batch)); + if (b == NULL) + return NULL; + + b->buf = buf; + b->limit = limit; + b->buflen = 0; + b->cur = buf; + b->overflow = false; + + return b; +} + +/** + * mnl_nlmsg_batch_stop - release a batch + * \param b pointer to batch + * + * This function releases the batch allocated by mnl_nlmsg_batch_start(). + */ +EXPORT_SYMBOL void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b) +{ + free(b); +} + +/** + * mnl_nlmsg_batch_next - get room for the next message in the batch + * \param b pointer to batch + * + * This function returns false if the last message did not fit into the + * batch. Otherwise, it prepares the batch to provide room for the new + * Netlink message in the batch and returns true. + * + * You have to put at least one message in the batch before calling this + * function, otherwise your application is likely to crash. + */ +EXPORT_SYMBOL bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b) +{ + struct nlmsghdr *nlh = b->cur; + + if (b->buflen + nlh->nlmsg_len > b->limit) { + b->overflow = true; + return false; + } + b->cur = b->buf + b->buflen + nlh->nlmsg_len; + b->buflen += nlh->nlmsg_len; + return true; +} + +/** + * mnl_nlmsg_batch_reset - reset the batch + * \param b pointer to batch + * + * This function allows you to reset a batch, so you can reuse it to create a + * new one. This function moves the last message which does not fit the batch to + * the head of the buffer, if any. + */ +EXPORT_SYMBOL void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b) +{ + if (b->overflow) { + struct nlmsghdr *nlh = b->cur; + memcpy(b->buf, b->cur, nlh->nlmsg_len); + b->buflen = nlh->nlmsg_len; + b->cur = b->buf + b->buflen; + b->overflow = false; + } else { + b->buflen = 0; + b->cur = b->buf; + } +} + +/** + * mnl_nlmsg_batch_size - get current size of the batch + * \param b pointer to batch + * + * This function returns the current size of the batch. + */ +EXPORT_SYMBOL size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b) +{ + return b->buflen; +} + +/** + * mnl_nlmsg_batch_head - get head of this batch + * \param b pointer to batch + * + * This function returns a pointer to the head of the batch, which is the + * beginning of the buffer that is used. + */ +EXPORT_SYMBOL void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b) +{ + return b->buf; +} + +/** + * mnl_nlmsg_batch_current - returns current position in the batch + * \param b pointer to batch + * + * This function returns a pointer to the current position in the buffer + * that is used to store the batch. + */ +EXPORT_SYMBOL void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b) +{ + return b->cur; +} + +/** + * mnl_nlmsg_batch_is_empty - check if there is any message in the batch + * \param b pointer to batch + * + * This function returns true if the batch is empty. + */ +EXPORT_SYMBOL bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b) +{ + return b->buflen == 0; +} + +/** + * @} + */ diff --git a/libmnl/src/socket.c b/libmnl/src/socket.c new file mode 100644 index 0000000..85b6bcc --- /dev/null +++ b/libmnl/src/socket.c @@ -0,0 +1,351 @@ +/* + * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include <libmnl/libmnl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <errno.h> +#include "internal.h" + +/** + * \mainpage + * + * libmnl is a minimalistic user-space library oriented to Netlink developers. + * There are a lot of common tasks in parsing, validating, constructing of + * both the Netlink header and TLVs that are repetitive and easy to get wrong. + * This library aims to provide simple helpers that allows you to avoid + * re-inventing the wheel in common Netlink tasks. + * + * \verbatim +"Simplify, simplify" -- Henry David Thoureau. Walden (1854) +\endverbatim + * + * The acronym libmnl stands for LIBrary Minimalistic NetLink. + * + * libmnl homepage is: + * http://www.netfilter.org/projects/libmnl/ + * + * \section features Main Features + * - Small: the shared library requires around 30KB for an x86-based computer. + * - Simple: this library avoids complex abstractions that tend to hide Netlink + * details. It avoids elaborated object-oriented infrastructure and complex + * callback-based workflow. + * - Easy to use: the library simplifies the work for Netlink-wise developers. + * It provides functions to make socket handling, message building, + * validating, parsing and sequence tracking, easier. + * - Easy to re-use: you can use this library to build your own abstraction + * layer upon this library, if you want to provide another library that + * hides Netlink details to your users. + * - Decoupling: the interdependency of the main bricks that compose this + * library is reduced, i.e. the library provides many helpers, but the + * programmer is not forced to use them. + * + * \section licensing Licensing terms + * This library is released under the LGPLv2.1 or any later (at your option). + * + * \section Dependencies + * You have to install the Linux kernel headers that you want to use to develop + * your application. Moreover, this library requires that you have some basics + * on Netlink. + * + * \section scm Git Tree + * The current development version of libmnl can be accessed at: + * https://git.netfilter.org/libmnl/ + * + * \section using Using libmnl + * You can access several example files under examples/ in the libmnl source + * code tree. + */ + +struct mnl_socket { + int fd; + struct sockaddr_nl addr; +}; + +/** + * \defgroup socket Netlink socket helpers + * @{ + */ + +/** + * mnl_socket_get_fd - obtain file descriptor from netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * + * This function returns the file descriptor of a given netlink socket. + */ +EXPORT_SYMBOL int mnl_socket_get_fd(const struct mnl_socket *nl) +{ + return nl->fd; +} + +/** + * mnl_socket_get_portid - obtain Netlink PortID from netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * + * This function returns the Netlink PortID of a given netlink socket. + * It's a common mistake to assume that this PortID equals the process ID + * which is not always true. This is the case if you open more than one + * socket that is binded to the same Netlink subsystem from the same process. + */ +EXPORT_SYMBOL unsigned int mnl_socket_get_portid(const struct mnl_socket *nl) +{ + return nl->addr.nl_pid; +} + +static struct mnl_socket *__mnl_socket_open(int bus, int flags) +{ + struct mnl_socket *nl; + + nl = calloc(1, sizeof(struct mnl_socket)); + if (nl == NULL) + return NULL; + + nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus); + if (nl->fd == -1) { + free(nl); + return NULL; + } + + return nl; +} + +/** + * mnl_socket_open - open a netlink socket + * \param bus the netlink socket bus ID (see NETLINK_* constants) + * + * On error, it returns NULL and errno is appropriately set. Otherwise, it + * returns a valid pointer to the mnl_socket structure. + */ +EXPORT_SYMBOL struct mnl_socket *mnl_socket_open(int bus) +{ + return __mnl_socket_open(bus, 0); +} + +/** + * mnl_socket_open2 - open a netlink socket with appropriate flags + * \param bus the netlink socket bus ID (see NETLINK_* constants) + * \param flags the netlink socket flags (see SOCK_* constants in socket(2)) + * + * This is similar to mnl_socket_open(), but allows one to set flags like + * SOCK_CLOEXEC at socket creation time (useful for multi-threaded programs + * performing exec calls). + * + * On error, it returns NULL and errno is appropriately set. Otherwise, it + * returns a valid pointer to the mnl_socket structure. + */ +EXPORT_SYMBOL struct mnl_socket *mnl_socket_open2(int bus, int flags) +{ + return __mnl_socket_open(bus, flags); +} + +/** + * mnl_socket_fdopen - associates a mnl_socket object with pre-existing socket. + * \param fd pre-existing socket descriptor. + * + * On error, it returns NULL and errno is appropriately set. Otherwise, it + * returns a valid pointer to the mnl_socket structure. It also sets the portID + * if the socket fd is already bound and it is AF_NETLINK. + * + * Note that mnl_socket_get_portid() returns 0 if this function is used with + * non-netlink socket. + */ +EXPORT_SYMBOL struct mnl_socket *mnl_socket_fdopen(int fd) +{ + int ret; + struct mnl_socket *nl; + struct sockaddr_nl addr; + socklen_t addr_len = sizeof(struct sockaddr_nl); + + ret = getsockname(fd, (struct sockaddr *) &addr, &addr_len); + if (ret == -1) + return NULL; + + nl = calloc(1, sizeof(struct mnl_socket)); + if (nl == NULL) + return NULL; + + nl->fd = fd; + if (addr.nl_family == AF_NETLINK) + nl->addr = addr; + + return nl; +} + +/** + * mnl_socket_bind - bind netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * \param groups the group of message you're interested in + * \param pid the port ID you want to use (use zero for automatic selection) + * + * On error, this function returns -1 and errno is appropriately set. On + * success, 0 is returned. You can use MNL_SOCKET_AUTOPID which is 0 for + * automatic port ID selection. + */ +EXPORT_SYMBOL int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, + pid_t pid) +{ + int ret; + socklen_t addr_len; + + nl->addr.nl_family = AF_NETLINK; + nl->addr.nl_groups = groups; + nl->addr.nl_pid = pid; + + ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr)); + if (ret < 0) + return ret; + + addr_len = sizeof(nl->addr); + ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len); + if (ret < 0) + return ret; + + if (addr_len != sizeof(nl->addr)) { + errno = EINVAL; + return -1; + } + if (nl->addr.nl_family != AF_NETLINK) { + errno = EINVAL; + return -1; + } + return 0; +} + +/** + * mnl_socket_sendto - send a netlink message of a certain size + * \param nl netlink socket obtained via mnl_socket_open() + * \param buf buffer containing the netlink message to be sent + * \param len number of bytes in the buffer that you want to send + * + * On error, it returns -1 and errno is appropriately set. Otherwise, it + * returns the number of bytes sent. + */ +EXPORT_SYMBOL ssize_t mnl_socket_sendto(const struct mnl_socket *nl, + const void *buf, size_t len) +{ + static const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + return sendto(nl->fd, buf, len, 0, + (struct sockaddr *) &snl, sizeof(snl)); +} + +/** + * mnl_socket_recvfrom - receive a netlink message + * \param nl netlink socket obtained via mnl_socket_open() + * \param buf buffer that you want to use to store the netlink message + * \param bufsiz size of the buffer passed to store the netlink message + * + * On error, it returns -1 and errno is appropriately set. If errno is set + * to ENOSPC, it means that the buffer that you have passed to store the + * netlink message is too small, so you have received a truncated message. + * To avoid this, you have to allocate a buffer of MNL_SOCKET_BUFFER_SIZE + * (which is 8KB, see linux/netlink.h for more information). Using this + * buffer size ensures that your buffer is big enough to store the netlink + * message without truncating it. + */ +EXPORT_SYMBOL ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, + void *buf, size_t bufsiz) +{ + ssize_t ret; + struct sockaddr_nl addr; + struct iovec iov = { + .iov_base = buf, + .iov_len = bufsiz, + }; + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + ret = recvmsg(nl->fd, &msg, 0); + if (ret == -1) + return ret; + + if (msg.msg_flags & MSG_TRUNC) { + errno = ENOSPC; + return -1; + } + if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { + errno = EINVAL; + return -1; + } + return ret; +} + +/** + * mnl_socket_close - close a given netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * + * On error, this function returns -1 and errno is appropriately set. + * On success, it returns 0. + */ +EXPORT_SYMBOL int mnl_socket_close(struct mnl_socket *nl) +{ + int ret = close(nl->fd); + free(nl); + return ret; +} + +/** + * mnl_socket_setsockopt - set Netlink socket option + * \param nl netlink socket obtained via mnl_socket_open() + * \param type type of Netlink socket options + * \param buf the buffer that contains the data about this option + * \param len the size of the buffer passed + * + * This function allows you to set some Netlink socket option. As of writing + * this (see linux/netlink.h), the existing options are: + * + * - \#define NETLINK_ADD_MEMBERSHIP 1 + * - \#define NETLINK_DROP_MEMBERSHIP 2 + * - \#define NETLINK_PKTINFO 3 + * - \#define NETLINK_BROADCAST_ERROR 4 + * - \#define NETLINK_NO_ENOBUFS 5 + * + * In the early days, Netlink only supported 32 groups expressed in a + * 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast + * groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to + * join a given multicast group. This function internally calls setsockopt() + * to join a given netlink multicast group. You can still use mnl_bind() + * and the 32-bit mask to join a set of Netlink multicast groups. + * + * On error, this function returns -1 and errno is appropriately set. + */ +EXPORT_SYMBOL int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, + void *buf, socklen_t len) +{ + return setsockopt(nl->fd, SOL_NETLINK, type, buf, len); +} + +/** + * mnl_socket_getsockopt - get a Netlink socket option + * \param nl netlink socket obtained via mnl_socket_open() + * \param type type of Netlink socket options + * \param buf pointer to the buffer to store the value of this option + * \param len size of the information written in the buffer + * + * On error, this function returns -1 and errno is appropriately set. + */ +EXPORT_SYMBOL int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, + void *buf, socklen_t *len) +{ + return getsockopt(nl->fd, SOL_NETLINK, type, buf, len); +} + +/** + * @} + */ @@ -0,0 +1,34 @@ +#ifndef ETHTOOL_LIST_H__ +#define ETHTOOL_LIST_H__ + +#include <unistd.h> + +/* Generic list utilities */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + head->next->prev = new; + new->next = head->next; + new->prev = head; + head->next = new; +} + +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + entry->next = NULL; + entry->prev = NULL; +} + +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#endif diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4 new file mode 100644 index 0000000..e8c5312 --- /dev/null +++ b/m4/ax_append_flag.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) +# +# DESCRIPTION +# +# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space +# added in between. +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains +# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly +# FLAG. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> +# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> +# +# 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 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <https://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 7 + +AC_DEFUN([AX_APPEND_FLAG], +[dnl +AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF +AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) +AS_VAR_SET_IF(FLAGS,[ + AS_CASE([" AS_VAR_GET(FLAGS) "], + [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], + [ + AS_VAR_APPEND(FLAGS,[" $1"]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) + ], + [ + AS_VAR_SET(FLAGS,[$1]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) +AS_VAR_POPDEF([FLAGS])dnl +])dnl AX_APPEND_FLAG diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..dcabb92 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> +# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> +# +# 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 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <https://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS @@ -31,23 +31,23 @@ static void dump_timer(const char *name, const void *p) static void dump_queue(const char *name, const void *a, int rx) { struct desc { - u_int32_t ctl; - u_int32_t next; - u_int32_t data_lo; - u_int32_t data_hi; - u_int32_t status; - u_int32_t timestamp; - u_int16_t csum2; - u_int16_t csum1; - u_int16_t csum2_start; - u_int16_t csum1_start; - u_int32_t addr_lo; - u_int32_t addr_hi; - u_int32_t count_lo; - u_int32_t count_hi; - u_int32_t byte_count; - u_int32_t csr; - u_int32_t flag; + uint32_t ctl; + uint32_t next; + uint32_t data_lo; + uint32_t data_hi; + uint32_t status; + uint32_t timestamp; + uint16_t csum2; + uint16_t csum1; + uint16_t csum2_start; + uint16_t csum1_start; + uint32_t addr_lo; + uint32_t addr_hi; + uint32_t count_lo; + uint32_t count_hi; + uint32_t byte_count; + uint32_t csr; + uint32_t flag; }; const struct desc *d = a; @@ -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; @@ -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 new file mode 100644 index 0000000..10ce8e9 --- /dev/null +++ b/netlink/bitset.c @@ -0,0 +1,259 @@ +/* + * bitset.h - netlink bitset helpers + * + * Functions for easier handling of ethtool netlink bitset attributes. + */ + +#include <stdio.h> +#include <errno.h> + +#include "../common.h" +#include "netlink.h" +#include "bitset.h" + +uint32_t bitset_get_count(const struct nlattr *bitset, int *retptr) +{ + const struct nlattr *attr; + + mnl_attr_for_each_nested(attr, bitset) { + if (mnl_attr_get_type(attr) != ETHTOOL_A_BITSET_SIZE) + continue; + *retptr = 0; + return mnl_attr_get_u32(attr); + } + + *retptr = -EFAULT; + return 0; +} + +bool bitset_is_compact(const struct nlattr *bitset) +{ + const struct nlattr *attr; + + mnl_attr_for_each_nested(attr, bitset) { + switch(mnl_attr_get_type(attr)) { + case ETHTOOL_A_BITSET_BITS: + return 0; + case ETHTOOL_A_BITSET_VALUE: + case ETHTOOL_A_BITSET_MASK: + return 1; + } + } + + return false; +} + +bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx, + int *retptr) +{ + const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(bitset_tb); + const struct nlattr *bits; + const struct nlattr *bit; + bool nomask; + int ret; + + *retptr = 0; + ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info); + 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) { + const uint32_t *bitmap = + (const uint32_t *)mnl_attr_get_payload(bits); + + if (idx >= 8 * mnl_attr_get_payload_len(bits)) + return false; + return bitmap[idx / 32] & (1U << (idx % 32)); + } + + bits = bitset_tb[ETHTOOL_A_BITSET_BITS]; + if (!bits) + goto err; + mnl_attr_for_each_nested(bit, bits) { + const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + unsigned int my_idx; + + if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT) + continue; + ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info); + if (ret < 0) + goto err; + ret = -EFAULT; + if (!tb[ETHTOOL_A_BITSET_BIT_INDEX]) + goto err; + + my_idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]); + if (my_idx == idx) + return mask || nomask || tb[ETHTOOL_A_BITSET_BIT_VALUE]; + } + + return false; +err: + fprintf(stderr, "malformed netlink message (bitset)\n"); + *retptr = ret; + return false; +} + +bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr) +{ + const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(bitset_tb); + const struct nlattr *bits; + const struct nlattr *bit; + int ret; + + *retptr = 0; + ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info); + if (ret < 0) + goto err; + + bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] : + bitset_tb[ETHTOOL_A_BITSET_VALUE]; + if (bits) { + const uint32_t *bitmap = + (const uint32_t *)mnl_attr_get_payload(bits); + unsigned int n = mnl_attr_get_payload_len(bits); + unsigned int i; + + ret = -EFAULT; + if (n % 4) + goto err; + for (i = 0; i < n / 4; i++) + if (bitmap[i]) + return false; + return true; + } + + bits = bitset_tb[ETHTOOL_A_BITSET_BITS]; + if (!bits) + goto err; + mnl_attr_for_each_nested(bit, bits) { + const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + + if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT) + continue; + if (mask || bitset_tb[ETHTOOL_A_BITSET_NOMASK]) + return false; + + ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info); + if (ret < 0) + goto err; + if (tb[ETHTOOL_A_BITSET_BIT_VALUE]) + return false; + } + + return true; +err: + fprintf(stderr, "malformed netlink message (bitset)\n"); + *retptr = ret; + 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) +{ + const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(bitset_tb); + const struct nlattr *bits; + const struct nlattr *bit; + bool is_list; + int ret; + + ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info); + if (ret < 0) + return ret; + is_list = bitset_tb[ETHTOOL_A_BITSET_NOMASK]; + + bits = bitset_tb[ETHTOOL_A_BITSET_VALUE]; + if (bits) { + const struct nlattr *mask = bitset_tb[ETHTOOL_A_BITSET_MASK]; + unsigned int count, nwords, idx; + uint32_t *val_bm; + uint32_t *mask_bm; + + if (!bitset_tb[ETHTOOL_A_BITSET_SIZE]) + return -EFAULT; + count = mnl_attr_get_u32(bitset_tb[ETHTOOL_A_BITSET_SIZE]); + nwords = (count + 31) / 32; + if ((mnl_attr_get_payload_len(bits) / 4 < nwords) || + (mask && mnl_attr_get_payload_len(mask) / 4 < nwords)) + return -EFAULT; + + val_bm = mnl_attr_get_payload(bits); + mask_bm = mask ? mnl_attr_get_payload(mask) : NULL; + for (idx = 0; idx < count; idx++) + if (!mask_bm || (mask_bm[idx / 32] & (1 << (idx % 32)))) + cb(idx, get_string(labels, idx), + val_bm[idx / 32] & (1 << (idx % 32)), data); + return 0; + } + + bits = bitset_tb[ETHTOOL_A_BITSET_BITS]; + if (!bits) + return -EFAULT; + mnl_attr_for_each_nested(bit, bits) { + const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + const char *name; + unsigned int idx; + + if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT) + continue; + + ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info); + if (ret < 0 || !tb[ETHTOOL_A_BITSET_BIT_INDEX] || + !tb[ETHTOOL_A_BITSET_BIT_NAME]) + return -EFAULT; + + idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]); + name = mnl_attr_get_str(tb[ETHTOOL_A_BITSET_BIT_NAME]); + cb(idx, name, is_list || tb[ETHTOOL_A_BITSET_BIT_VALUE], data); + } + + return 0; +} diff --git a/netlink/bitset.h b/netlink/bitset.h new file mode 100644 index 0000000..4c9cdac --- /dev/null +++ b/netlink/bitset.h @@ -0,0 +1,28 @@ +/* + * bitset.h - netlink bitset helpers + * + * Declarations of helpers for handling ethtool netlink bitsets. + */ + +#ifndef ETHTOOL_NETLINK_BITSET_H__ +#define ETHTOOL_NETLINK_BITSET_H__ + +#include <libmnl/libmnl.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#include <linux/ethtool_netlink.h> +#include "strset.h" + +typedef void (*bitset_walk_callback)(unsigned int, const char *, bool, void *); + +uint32_t bitset_get_count(const struct nlattr *bitset, int *retptr); +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); + +#endif /* ETHTOOL_NETLINK_BITSET_H__ */ diff --git a/netlink/cable_test.c b/netlink/cable_test.c new file mode 100644 index 0000000..9305a47 --- /dev/null +++ b/netlink/cable_test.c @@ -0,0 +1,595 @@ +/* + * 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; + nlctx->filter_devname = ctx->devname; + + 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; + nlctx->filter_devname = ctx->devname; + + 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..5cae227 --- /dev/null +++ b/netlink/channels.c @@ -0,0 +1,143 @@ +/* + * 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("rx-max", "RX:\t\t", tb[ETHTOOL_A_CHANNELS_RX_MAX]); + show_u32("tx-max", "TX:\t\t", tb[ETHTOOL_A_CHANNELS_TX_MAX]); + show_u32("other-max", "Other:\t\t", tb[ETHTOOL_A_CHANNELS_OTHER_MAX]); + show_u32("combined-max", "Combined:\t", + tb[ETHTOOL_A_CHANNELS_COMBINED_MAX]); + printf("Current hardware settings:\n"); + show_u32("rx", "RX:\t\t", tb[ETHTOOL_A_CHANNELS_RX_COUNT]); + show_u32("tx", "TX:\t\t", tb[ETHTOOL_A_CHANNELS_TX_COUNT]); + show_u32("other", "Other:\t\t", tb[ETHTOOL_A_CHANNELS_OTHER_COUNT]); + show_u32("combined", "Combined:\t", + tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT]); + + 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..bc34d3d --- /dev/null +++ b/netlink/coalesce.c @@ -0,0 +1,335 @@ +/* + * 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; + + open_json_object(NULL); + + if (silent) + show_cr(); + print_string(PRINT_ANY, "ifname", "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("stats-block-usecs", "stats-block-usecs:\t", + tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS]); + show_u32("sample-interval", "sample-interval:\t", + tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL]); + show_u32("pkt-rate-low", "pkt-rate-low:\t\t", + tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW]); + show_u32("pkt-rate-high", "pkt-rate-high:\t\t", + tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH]); + show_cr(); + show_u32("rx-usecs", "rx-usecs:\t", tb[ETHTOOL_A_COALESCE_RX_USECS]); + show_u32("rx-frames", "rx-frames:\t", + tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES]); + show_u32("rx-usecs-irq", "rx-usecs-irq:\t", + tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ]); + show_u32("rx-frames-irq", "rx-frames-irq:\t", + tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ]); + show_cr(); + show_u32("tx-usecs", "tx-usecs:\t", tb[ETHTOOL_A_COALESCE_TX_USECS]); + show_u32("tx-frames", "tx-frames:\t", + tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES]); + show_u32("tx-usecs-irq", "tx-usecs-irq:\t", + tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ]); + show_u32("tx-frames-irq", "tx-frames-irq:\t", + tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ]); + show_cr(); + show_u32("rx-usecs-low", "rx-usecs-low:\t", + tb[ETHTOOL_A_COALESCE_RX_USECS_LOW]); + show_u32("rx-frame-low", "rx-frame-low:\t", + tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW]); + show_u32("tx-usecs-low", "tx-usecs-low:\t", + tb[ETHTOOL_A_COALESCE_TX_USECS_LOW]); + show_u32("tx-frame-low", "tx-frame-low:\t", + tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW]); + show_cr(); + show_u32("rx-usecs-high", "rx-usecs-high:\t", + tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH]); + show_u32("rx-frame-high", "rx-frame-high:\t", + tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH]); + show_u32("tx-usecs-high", "tx-usecs-high:\t", + tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH]); + show_u32("tx-frame-high", "tx-frame-high:\t", + tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH]); + show_cr(); + show_bool("rx", "CQE mode RX: %s ", + tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX]); + show_bool("tx", "TX: %s\n", tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX]); + show_cr(); + show_u32("tx-aggr-max-bytes", "tx-aggr-max-bytes:\t", + tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES]); + show_u32("tx-aggr-max-frames", "tx-aggr-max-frames:\t", + tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES]); + show_u32("tx-aggr-time-usecs", "tx-aggr-time-usecs\t", + tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS]); + show_cr(); + + close_json_object(); + + 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; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, coalesce_reply_cb); + delete_json_obj(); + return ret; +} + +/* 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, + }, + { + .arg = "cqe-mode-rx", + .type = ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "cqe-mode-tx", + .type = ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "tx-aggr-max-bytes", + .type = ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-aggr-max-frames", + .type = ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-aggr-time-usecs", + .type = ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, + .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 new file mode 100644 index 0000000..661de26 --- /dev/null +++ b/netlink/desc-ethtool.c @@ -0,0 +1,595 @@ +/* + * desc-ethtool.c - ethtool netlink format descriptions + * + * Descriptions of ethtool netlink messages and attributes for pretty print. + */ + +#include "../internal.h" +#include <linux/ethtool_netlink.h> + +#include "prettymsg.h" + +static const struct pretty_nla_desc __header_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_HEADER_UNSPEC), + NLATTR_DESC_U32(ETHTOOL_A_HEADER_DEV_INDEX), + NLATTR_DESC_STRING(ETHTOOL_A_HEADER_DEV_NAME), + NLATTR_DESC_X32(ETHTOOL_A_HEADER_FLAGS), +}; + +static const struct pretty_nla_desc __bitset_bit_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_BITSET_BIT_UNSPEC), + NLATTR_DESC_U32(ETHTOOL_A_BITSET_BIT_INDEX), + NLATTR_DESC_STRING(ETHTOOL_A_BITSET_BIT_NAME), + NLATTR_DESC_FLAG(ETHTOOL_A_BITSET_BIT_VALUE), +}; + +static const struct pretty_nla_desc __bitset_bits_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_BITSET_BITS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_BITSET_BITS_BIT, bitset_bit), +}; + +static const struct pretty_nla_desc __bitset_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_BITSET_UNSPEC), + NLATTR_DESC_FLAG(ETHTOOL_A_BITSET_NOMASK), + NLATTR_DESC_U32(ETHTOOL_A_BITSET_SIZE), + NLATTR_DESC_NESTED(ETHTOOL_A_BITSET_BITS, bitset_bits), + NLATTR_DESC_BINARY(ETHTOOL_A_BITSET_VALUE), + NLATTR_DESC_BINARY(ETHTOOL_A_BITSET_MASK), +}; + +static const struct pretty_nla_desc __string_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_STRING_UNSPEC), + NLATTR_DESC_U32(ETHTOOL_A_STRING_INDEX), + NLATTR_DESC_STRING(ETHTOOL_A_STRING_VALUE), +}; + +static const struct pretty_nla_desc __strings_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_STRINGS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_STRINGS_STRING, string), +}; + +static const struct pretty_nla_desc __stringset_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_STRINGSET_UNSPEC), + NLATTR_DESC_U32(ETHTOOL_A_STRINGSET_ID), + NLATTR_DESC_U32(ETHTOOL_A_STRINGSET_COUNT), + NLATTR_DESC_NESTED(ETHTOOL_A_STRINGSET_STRINGS, strings), +}; + +static const struct pretty_nla_desc __stringsets_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_STRINGSETS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_STRINGSETS_STRINGSET, stringset), +}; + +static const struct pretty_nla_desc __strset_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_STRSET_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_STRSET_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_STRSET_STRINGSETS, stringsets), + NLATTR_DESC_FLAG(ETHTOOL_A_STRSET_COUNTS_ONLY), +}; + +static const struct pretty_nla_desc __linkinfo_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_LINKINFO_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_LINKINFO_HEADER, header), + NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_PORT), + NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_PHYADDR), + NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_TP_MDIX), + NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_TP_MDIX_CTRL), + NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_TRANSCEIVER), +}; + +static const char *__linkmodes_rate_matching_names[] = { + [RATE_MATCH_NONE] = "RATE_MATCH_NONE", + [RATE_MATCH_PAUSE] = "RATE_MATCH_PAUSE", + [RATE_MATCH_CRS] = "RATE_MATCH_CRS", + [RATE_MATCH_OPEN_LOOP] = "RATE_MATCH_OPEN_LOOP", +}; + +static const struct pretty_nla_desc __linkmodes_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_LINKMODES_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_LINKMODES_HEADER, header), + NLATTR_DESC_BOOL(ETHTOOL_A_LINKMODES_AUTONEG), + NLATTR_DESC_NESTED(ETHTOOL_A_LINKMODES_OURS, bitset), + 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), + NLATTR_DESC_U32(ETHTOOL_A_LINKMODES_LANES), + NLATTR_DESC_U8_ENUM(ETHTOOL_A_LINKMODES_RATE_MATCHING, + linkmodes_rate_matching), +}; + +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[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_DEBUG_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_DEBUG_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_DEBUG_MSGMASK, bitset), +}; + +static const struct pretty_nla_desc __wol_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_WOL_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_WOL_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_WOL_MODES, bitset), + 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 char *__rings_tcp_data_split_names[] = { + [ETHTOOL_TCP_DATA_SPLIT_UNKNOWN] = "ETHTOOL_TCP_DATA_SPLIT_UNKNOWN", + [ETHTOOL_TCP_DATA_SPLIT_DISABLED] = "ETHTOOL_TCP_DATA_SPLIT_DISABLED", + [ETHTOOL_TCP_DATA_SPLIT_ENABLED] = "ETHTOOL_TCP_DATA_SPLIT_ENABLED", +}; + +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), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_BUF_LEN), + NLATTR_DESC_U8_ENUM(ETHTOOL_A_RINGS_TCP_DATA_SPLIT, rings_tcp_data_split), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_CQE_SIZE), + NLATTR_DESC_BOOL(ETHTOOL_A_RINGS_TX_PUSH), + NLATTR_DESC_BOOL(ETHTOOL_A_RINGS_RX_PUSH), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX), +}; + +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), + NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_CQE_MODE_TX), + NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_CQE_MODE_RX), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS), +}; + +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_nla_desc __fec_stats_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_FEC_STAT_UNSPEC), + NLATTR_DESC_BINARY(ETHTOOL_A_FEC_STAT_PAD), + NLATTR_DESC_U64(ETHTOOL_A_FEC_STAT_CORRECTED), + NLATTR_DESC_U64(ETHTOOL_A_FEC_STAT_UNCORR), + NLATTR_DESC_U64(ETHTOOL_A_FEC_STAT_CORR_BITS), +}; + +static const struct pretty_nla_desc __fec_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_FEC_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_FEC_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_FEC_MODES, bitset), + NLATTR_DESC_BOOL(ETHTOOL_A_FEC_AUTO), + NLATTR_DESC_U32(ETHTOOL_A_FEC_ACTIVE), + NLATTR_DESC_NESTED(ETHTOOL_A_FEC_STATS, fec_stats), +}; + +const struct pretty_nla_desc __module_eeprom_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_MODULE_EEPROM_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_MODULE_EEPROM_HEADER, header), + NLATTR_DESC_U32(ETHTOOL_A_MODULE_EEPROM_OFFSET), + NLATTR_DESC_U32(ETHTOOL_A_MODULE_EEPROM_LENGTH), + NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_PAGE), + NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_BANK), + NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS), + NLATTR_DESC_BINARY(ETHTOOL_A_MODULE_EEPROM_DATA) +}; + +static const struct pretty_nla_desc __stats_grp_stat_desc[] = { + NLATTR_DESC_U64(0), NLATTR_DESC_U64(1), NLATTR_DESC_U64(2), + NLATTR_DESC_U64(3), NLATTR_DESC_U64(4), NLATTR_DESC_U64(5), + NLATTR_DESC_U64(6), NLATTR_DESC_U64(7), NLATTR_DESC_U64(8), + NLATTR_DESC_U64(9), NLATTR_DESC_U64(10), NLATTR_DESC_U64(11), + NLATTR_DESC_U64(12), NLATTR_DESC_U64(13), NLATTR_DESC_U64(14), + NLATTR_DESC_U64(15), NLATTR_DESC_U64(16), NLATTR_DESC_U64(17), + NLATTR_DESC_U64(18), NLATTR_DESC_U64(19), NLATTR_DESC_U64(20), + NLATTR_DESC_U64(21), NLATTR_DESC_U64(22), NLATTR_DESC_U64(23), + NLATTR_DESC_U64(24), NLATTR_DESC_U64(25), NLATTR_DESC_U64(26), + NLATTR_DESC_U64(27), NLATTR_DESC_U64(28), NLATTR_DESC_U64(29), +}; + +static const struct pretty_nla_desc __stats_grp_hist_desc[] = { + NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_HIST_BKT_LOW), + NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_HIST_BKT_HI), + NLATTR_DESC_U64(ETHTOOL_A_STATS_GRP_HIST_VAL), +}; + +static const struct pretty_nla_desc __stats_grp_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_STATS_GRP_UNSPEC), + NLATTR_DESC_INVALID(ETHTOOL_A_STATS_GRP_PAD), + NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_ID), + NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_SS_ID), + NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP_STAT, stats_grp_stat), + NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP_HIST_RX, stats_grp_hist), + NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP_HIST_TX, stats_grp_hist), +}; + +static const struct pretty_nla_desc __stats_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_STATS_UNSPEC), + NLATTR_DESC_INVALID(ETHTOOL_A_STATS_PAD), + NLATTR_DESC_NESTED(ETHTOOL_A_STATS_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GROUPS, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP, stats_grp), +}; + +static const struct pretty_nla_desc __phc_vclocks_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_PHC_VCLOCKS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_PHC_VCLOCKS_HEADER, header), + NLATTR_DESC_U32(ETHTOOL_A_PHC_VCLOCKS_NUM), + NLATTR_DESC_BINARY(ETHTOOL_A_PHC_VCLOCKS_INDEX), +}; + +static const struct pretty_nla_desc __module_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_MODULE_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_MODULE_HEADER, header), + NLATTR_DESC_U8(ETHTOOL_A_MODULE_POWER_MODE_POLICY), + NLATTR_DESC_U8(ETHTOOL_A_MODULE_POWER_MODE), +}; + +static const char *__pse_admin_state_names[] = { + [ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN] = "ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN", + [ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED] = "ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED", + [ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED] = "ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED", +}; + +static const char *__pse_pw_d_status_names[] = { + [ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN] = "ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN", + [ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED] = "ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED", + [ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING] = "ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING", + [ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING] = "ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING", + [ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP] = "ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP", + [ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE] = "ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE", + [ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR] = "ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR", +}; + +static const struct pretty_nla_desc __pse_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_PSE_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_PSE_HEADER, header), + NLATTR_DESC_U32_ENUM(ETHTOOL_A_PODL_PSE_ADMIN_STATE, pse_admin_state), + NLATTR_DESC_U32_ENUM(ETHTOOL_A_PODL_PSE_ADMIN_CONTROL, pse_admin_state), + NLATTR_DESC_U32_ENUM(ETHTOOL_A_PODL_PSE_PW_D_STATUS, pse_pw_d_status), +}; + +static const struct pretty_nla_desc __rss_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_RSS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_RSS_HEADER, header), + NLATTR_DESC_U32(ETHTOOL_A_RSS_CONTEXT), + NLATTR_DESC_U32(ETHTOOL_A_RSS_HFUNC), + NLATTR_DESC_BINARY(ETHTOOL_A_RSS_INDIR), + NLATTR_DESC_BINARY(ETHTOOL_A_RSS_HKEY), +}; + +static const struct pretty_nla_desc __plca_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_PLCA_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_PLCA_HEADER, header), + NLATTR_DESC_U16(ETHTOOL_A_PLCA_VERSION), + NLATTR_DESC_U8(ETHTOOL_A_PLCA_ENABLED), + NLATTR_DESC_U8(ETHTOOL_A_PLCA_STATUS), + NLATTR_DESC_U32(ETHTOOL_A_PLCA_NODE_CNT), + NLATTR_DESC_U32(ETHTOOL_A_PLCA_NODE_ID), + NLATTR_DESC_U32(ETHTOOL_A_PLCA_TO_TMR), + NLATTR_DESC_U32(ETHTOOL_A_PLCA_BURST_CNT), + NLATTR_DESC_U32(ETHTOOL_A_PLCA_BURST_TMR), +}; + +static const struct pretty_nla_desc __mm_stat_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_MM_STAT_UNSPEC), + NLATTR_DESC_BINARY(ETHTOOL_A_MM_STAT_PAD), + NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS), + NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_SMD_ERRORS), + NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_REASSEMBLY_OK), + NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_RX_FRAG_COUNT), + NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_TX_FRAG_COUNT), + NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_HOLD_COUNT), +}; + +static const struct pretty_nla_desc __mm_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_MM_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_MM_HEADER, header), + NLATTR_DESC_U8(ETHTOOL_A_MM_PMAC_ENABLED), + NLATTR_DESC_U8(ETHTOOL_A_MM_TX_ENABLED), + NLATTR_DESC_U8(ETHTOOL_A_MM_TX_ACTIVE), + NLATTR_DESC_U32(ETHTOOL_A_MM_TX_MIN_FRAG_SIZE), + NLATTR_DESC_U32(ETHTOOL_A_MM_RX_MIN_FRAG_SIZE), + NLATTR_DESC_U8(ETHTOOL_A_MM_VERIFY_ENABLED), + NLATTR_DESC_U8(ETHTOOL_A_MM_VERIFY_STATUS), + NLATTR_DESC_U32(ETHTOOL_A_MM_VERIFY_TIME), + NLATTR_DESC_U32(ETHTOOL_A_MM_MAX_VERIFY_TIME), + NLATTR_DESC_NESTED(ETHTOOL_A_MM_STATS, mm_stat), +}; + +const struct pretty_nlmsg_desc ethnl_umsg_desc[] = { + NLMSG_DESC_INVALID(ETHTOOL_MSG_USER_NONE), + NLMSG_DESC(ETHTOOL_MSG_STRSET_GET, strset), + NLMSG_DESC(ETHTOOL_MSG_LINKINFO_GET, linkinfo), + NLMSG_DESC(ETHTOOL_MSG_LINKINFO_SET, linkinfo), + NLMSG_DESC(ETHTOOL_MSG_LINKMODES_GET, linkmodes), + NLMSG_DESC(ETHTOOL_MSG_LINKMODES_SET, linkmodes), + NLMSG_DESC(ETHTOOL_MSG_LINKSTATE_GET, linkstate), + NLMSG_DESC(ETHTOOL_MSG_DEBUG_GET, debug), + 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), + NLMSG_DESC(ETHTOOL_MSG_FEC_GET, fec), + NLMSG_DESC(ETHTOOL_MSG_FEC_SET, fec), + NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_GET, module_eeprom), + NLMSG_DESC(ETHTOOL_MSG_STATS_GET, stats), + NLMSG_DESC(ETHTOOL_MSG_PHC_VCLOCKS_GET, phc_vclocks), + NLMSG_DESC(ETHTOOL_MSG_MODULE_GET, module), + NLMSG_DESC(ETHTOOL_MSG_MODULE_SET, module), + NLMSG_DESC(ETHTOOL_MSG_PSE_GET, pse), + NLMSG_DESC(ETHTOOL_MSG_PSE_SET, pse), + NLMSG_DESC(ETHTOOL_MSG_RSS_GET, rss), + NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_CFG, plca), + NLMSG_DESC(ETHTOOL_MSG_PLCA_SET_CFG, plca), + NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_STATUS, plca), + NLMSG_DESC(ETHTOOL_MSG_MM_GET, mm), + NLMSG_DESC(ETHTOOL_MSG_MM_SET, mm), +}; + +const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc); + +const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = { + NLMSG_DESC_INVALID(ETHTOOL_MSG_KERNEL_NONE), + NLMSG_DESC(ETHTOOL_MSG_STRSET_GET_REPLY, strset), + NLMSG_DESC(ETHTOOL_MSG_LINKINFO_GET_REPLY, linkinfo), + NLMSG_DESC(ETHTOOL_MSG_LINKINFO_NTF, linkinfo), + NLMSG_DESC(ETHTOOL_MSG_LINKMODES_GET_REPLY, linkmodes), + NLMSG_DESC(ETHTOOL_MSG_LINKMODES_NTF, linkmodes), + NLMSG_DESC(ETHTOOL_MSG_LINKSTATE_GET_REPLY, linkstate), + NLMSG_DESC(ETHTOOL_MSG_DEBUG_GET_REPLY, debug), + 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), + NLMSG_DESC(ETHTOOL_MSG_FEC_GET_REPLY, fec), + NLMSG_DESC(ETHTOOL_MSG_FEC_NTF, fec), + NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY, module_eeprom), + NLMSG_DESC(ETHTOOL_MSG_STATS_GET_REPLY, stats), + NLMSG_DESC(ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY, phc_vclocks), + NLMSG_DESC(ETHTOOL_MSG_MODULE_GET_REPLY, module), + NLMSG_DESC(ETHTOOL_MSG_MODULE_NTF, module), + NLMSG_DESC(ETHTOOL_MSG_PSE_GET_REPLY, pse), + NLMSG_DESC(ETHTOOL_MSG_RSS_GET_REPLY, rss), + NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_CFG_REPLY, plca), + NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_STATUS_REPLY, plca), + NLMSG_DESC(ETHTOOL_MSG_PLCA_NTF, plca), + NLMSG_DESC(ETHTOOL_MSG_MM_GET_REPLY, mm), + NLMSG_DESC(ETHTOOL_MSG_MM_NTF, mm), +}; + +const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc); diff --git a/netlink/desc-genlctrl.c b/netlink/desc-genlctrl.c new file mode 100644 index 0000000..43b41ab --- /dev/null +++ b/netlink/desc-genlctrl.c @@ -0,0 +1,113 @@ +/* + * desc-genlctrl.c - genetlink control format descriptions + * + * Descriptions of genetlink control messages and attributes for pretty print. + */ + +#include <linux/genetlink.h> + +#include "../internal.h" +#include "prettymsg.h" + +static const struct pretty_nla_desc __attrop_desc[] = { + NLATTR_DESC_INVALID(CTRL_ATTR_OP_UNSPEC), + NLATTR_DESC_U32(CTRL_ATTR_OP_ID), + NLATTR_DESC_X32(CTRL_ATTR_OP_FLAGS), +}; + +static const struct pretty_nla_desc __attrops_desc[] = { + NLATTR_DESC_NESTED(0, attrop), +}; + +static const struct pretty_nla_desc __mcgrp_desc[] = { + NLATTR_DESC_INVALID(CTRL_ATTR_MCAST_GRP_UNSPEC), + NLATTR_DESC_STRING(CTRL_ATTR_MCAST_GRP_NAME), + NLATTR_DESC_U32(CTRL_ATTR_MCAST_GRP_ID), +}; + +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), + NLATTR_DESC_STRING(CTRL_ATTR_FAMILY_NAME), + NLATTR_DESC_U32(CTRL_ATTR_VERSION), + NLATTR_DESC_U32(CTRL_ATTR_HDRSIZE), + 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[] = { + NLMSG_DESC_INVALID(CTRL_CMD_UNSPEC), + NLMSG_DESC(CTRL_CMD_NEWFAMILY, attr), + NLMSG_DESC(CTRL_CMD_DELFAMILY, attr), + NLMSG_DESC(CTRL_CMD_GETFAMILY, attr), + NLMSG_DESC(CTRL_CMD_NEWOPS, attr), + NLMSG_DESC(CTRL_CMD_DELOPS, attr), + NLMSG_DESC(CTRL_CMD_GETOPS, attr), + 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/desc-rtnl.c b/netlink/desc-rtnl.c new file mode 100644 index 0000000..e15e6f0 --- /dev/null +++ b/netlink/desc-rtnl.c @@ -0,0 +1,96 @@ +/* + * desc-rtnl.c - rtnetlink message descriptions + * + * Descriptions of rtnetlink messages and attributes for pretty print. + */ + +#include <linux/rtnetlink.h> + +#include "../internal.h" +#include "prettymsg.h" + +static const struct pretty_nla_desc __link_desc[] = { + NLATTR_DESC_INVALID(IFLA_UNSPEC), + NLATTR_DESC_BINARY(IFLA_ADDRESS), + NLATTR_DESC_BINARY(IFLA_BROADCAST), + NLATTR_DESC_STRING(IFLA_IFNAME), + NLATTR_DESC_U32(IFLA_MTU), + NLATTR_DESC_U32(IFLA_LINK), + NLATTR_DESC_STRING(IFLA_QDISC), + NLATTR_DESC_BINARY(IFLA_STATS), + NLATTR_DESC_INVALID(IFLA_COST), + NLATTR_DESC_INVALID(IFLA_PRIORITY), + NLATTR_DESC_U32(IFLA_MASTER), + NLATTR_DESC_BINARY(IFLA_WIRELESS), + NLATTR_DESC_NESTED_NODESC(IFLA_PROTINFO), + NLATTR_DESC_U32(IFLA_TXQLEN), + NLATTR_DESC_BINARY(IFLA_MAP), + NLATTR_DESC_U32(IFLA_WEIGHT), + NLATTR_DESC_U8(IFLA_OPERSTATE), + NLATTR_DESC_U8(IFLA_LINKMODE), + NLATTR_DESC_NESTED_NODESC(IFLA_LINKINFO), + NLATTR_DESC_U32(IFLA_NET_NS_PID), + NLATTR_DESC_STRING(IFLA_IFALIAS), + NLATTR_DESC_U32(IFLA_NUM_VF), + NLATTR_DESC_NESTED_NODESC(IFLA_VFINFO_LIST), + NLATTR_DESC_BINARY(IFLA_STATS64), + NLATTR_DESC_NESTED_NODESC(IFLA_VF_PORTS), + NLATTR_DESC_NESTED_NODESC(IFLA_PORT_SELF), + NLATTR_DESC_NESTED_NODESC(IFLA_AF_SPEC), + NLATTR_DESC_U32(IFLA_GROUP), + NLATTR_DESC_U32(IFLA_NET_NS_FD), + NLATTR_DESC_U32(IFLA_EXT_MASK), + NLATTR_DESC_U32(IFLA_PROMISCUITY), + NLATTR_DESC_U32(IFLA_NUM_TX_QUEUES), + NLATTR_DESC_U32(IFLA_NUM_RX_QUEUES), + NLATTR_DESC_U8(IFLA_CARRIER), + NLATTR_DESC_BINARY(IFLA_PHYS_PORT_ID), + NLATTR_DESC_U32(IFLA_CARRIER_CHANGES), + NLATTR_DESC_BINARY(IFLA_PHYS_SWITCH_ID), + NLATTR_DESC_S32(IFLA_LINK_NETNSID), + NLATTR_DESC_STRING(IFLA_PHYS_PORT_NAME), + NLATTR_DESC_U8(IFLA_PROTO_DOWN), + NLATTR_DESC_U32(IFLA_GSO_MAX_SEGS), + NLATTR_DESC_U32(IFLA_GSO_MAX_SIZE), + NLATTR_DESC_BINARY(IFLA_PAD), + NLATTR_DESC_U32(IFLA_XDP), + NLATTR_DESC_U32(IFLA_EVENT), + NLATTR_DESC_S32(IFLA_NEW_NETNSID), + NLATTR_DESC_S32(IFLA_IF_NETNSID), + NLATTR_DESC_U32(IFLA_CARRIER_UP_COUNT), + NLATTR_DESC_U32(IFLA_CARRIER_DOWN_COUNT), + NLATTR_DESC_S32(IFLA_NEW_IFINDEX), + NLATTR_DESC_U32(IFLA_MIN_MTU), + NLATTR_DESC_U32(IFLA_MAX_MTU), + NLATTR_DESC_NESTED_NODESC(IFLA_PROP_LIST), + NLATTR_DESC_STRING(IFLA_ALT_IFNAME), + NLATTR_DESC_BINARY(IFLA_PERM_ADDRESS), +}; + +const struct pretty_nlmsg_desc rtnl_msg_desc[] = { + NLMSG_DESC(RTM_NEWLINK, link), + NLMSG_DESC(RTM_DELLINK, link), + NLMSG_DESC(RTM_GETLINK, link), + NLMSG_DESC(RTM_SETLINK, link), +}; + +const unsigned int rtnl_msg_n_desc = ARRAY_SIZE(rtnl_msg_desc); + +#define RTNL_MSGHDR_LEN(_name, _struct) \ + [((RTM_ ## _name) - RTM_BASE) / 4] = sizeof(struct _struct) +#define RTNL_MSGHDR_NOLEN(_name) \ + [((RTM_ ## _name) - RTM_BASE) / 4] = USHRT_MAX + +const unsigned short rtnl_msghdr_lengths[] = { + RTNL_MSGHDR_LEN(NEWLINK, ifinfomsg), + RTNL_MSGHDR_LEN(NEWADDR, ifaddrmsg), + RTNL_MSGHDR_LEN(NEWROUTE, rtmsg), + RTNL_MSGHDR_LEN(NEWNEIGH, ndmsg), + RTNL_MSGHDR_LEN(NEWRULE, rtmsg), + RTNL_MSGHDR_LEN(NEWQDISC, tcmsg), + RTNL_MSGHDR_LEN(NEWTCLASS, tcmsg), + RTNL_MSGHDR_LEN(NEWTFILTER, tcmsg), + RTNL_MSGHDR_LEN(NEWACTION, tcamsg), +}; + +const unsigned int rtnl_msghdr_n_len = ARRAY_SIZE(rtnl_msghdr_lengths); 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 new file mode 100644 index 0000000..e2d6b71 --- /dev/null +++ b/netlink/extapi.h @@ -0,0 +1,136 @@ +/* + * extapi.h - external interface of netlink code + * + * Declarations needed by non-netlink code (mostly ethtool.c). + */ + +#ifndef ETHTOOL_EXTAPI_H__ +#define ETHTOOL_EXTAPI_H__ + +struct cmd_context; +struct nl_context; + +typedef int (*nl_func_t)(struct cmd_context *); +typedef bool (*nl_chk_t)(struct cmd_context *); + +#ifdef ETHTOOL_ENABLE_NETLINK + +void netlink_run_handler(struct cmd_context *ctx, nl_chk_t nlchk, + 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_gfec(struct cmd_context *ctx); +int nl_sfec(struct cmd_context *ctx); +bool nl_gstats_chk(struct cmd_context *ctx); +int nl_gstats(struct cmd_context *ctx); +int nl_gmodule(struct cmd_context *ctx); +int nl_smodule(struct cmd_context *ctx); +int nl_monitor(struct cmd_context *ctx); +int nl_getmodule(struct cmd_context *ctx); +int nl_grss(struct cmd_context *ctx); +int nl_plca_get_cfg(struct cmd_context *ctx); +int nl_plca_set_cfg(struct cmd_context *ctx); +int nl_plca_get_status(struct cmd_context *ctx); +int nl_get_mm(struct cmd_context *ctx); +int nl_set_mm(struct cmd_context *ctx); +int nl_gpse(struct cmd_context *ctx); +int nl_spse(struct cmd_context *ctx); + +void nl_monitor_usage(void); + +int nl_get_eeprom_page(struct cmd_context *ctx, + struct ethtool_module_eeprom *request); + +#else /* ETHTOOL_ENABLE_NETLINK */ + +static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused, + nl_chk_t nlchk __maybe_unused, + nl_func_t nlfunc __maybe_unused, + bool no_fallback) +{ + if (no_fallback) { + fprintf(stderr, + "Command requires kernel netlink support which is not " + "enabled in this ethtool binary\n"); + exit(1); + } +} + +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) +{ +} + +static inline int +nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused, + struct ethtool_module_eeprom *request __maybe_unused) +{ + fprintf(stderr, "Netlink not supported by ethtool.\n"); + return -EOPNOTSUPP; +} + +#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 +#define nl_gfec NULL +#define nl_sfec NULL +#define nl_gstats_chk NULL +#define nl_gstats NULL +#define nl_getmodule NULL +#define nl_gmodule NULL +#define nl_smodule NULL +#define nl_grss NULL +#define nl_plca_get_cfg NULL +#define nl_plca_set_cfg NULL +#define nl_plca_get_status NULL +#define nl_get_mm NULL +#define nl_set_mm NULL +#define nl_gpse NULL +#define nl_spse NULL + +#endif /* ETHTOOL_ENABLE_NETLINK */ + +#endif /* ETHTOOL_EXTAPI_H__ */ diff --git a/netlink/features.c b/netlink/features.c new file mode 100644 index 0000000..5711ff4 --- /dev/null +++ b/netlink/features.c @@ -0,0 +1,569 @@ +/* + * 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]"; + if (is_json_context()) { + open_json_object(name); + print_bool(PRINT_JSON, "active", NULL, feature_on(results->active, idx)); + print_bool(PRINT_JSON, "fixed", NULL, + (!feature_on(results->hw, idx) || feature_on(results->nochange, idx))); + print_bool(PRINT_JSON, "requested", NULL, feature_on(results->wanted, idx)); + close_json_object(); + } else { + 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) { + if (is_json_context()) { + open_json_object(off_flag_def[i].long_name); + print_bool(PRINT_JSON, "active", NULL, flag_value); + print_null(PRINT_JSON, "fixed", NULL, NULL); + print_null(PRINT_JSON, "requested", NULL, NULL); + close_json_object(); + } else { + 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'); + open_json_object(NULL); + print_string(PRINT_ANY, "ifname", "Features for %s:\n", nlctx->devname); + ret = dump_features(tb, feature_names); + close_json_object(); + 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; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, features_reply_cb); + delete_json_obj(); + + return ret; +} + +/* FEATURES_SET */ + +struct sfeatures_context { + bool nothing_changed; + uint32_t req_mask[]; +}; + +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) { + free(sfctx); + return 2; + } + if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEATURES_HEADER, ctx->devname, + ETHTOOL_FLAG_COMPACT_BITSETS)) { + free(sfctx); + return -EMSGSIZE; + } + ret = fill_sfeatures_bitmap(nlctx, feature_names); + if (ret < 0) { + free(sfctx); + return ret; + } + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) { + free(sfctx); + return 92; + } + ret = nlsock_process_reply(nlsk, sfeatures_reply_cb, nlctx); + if (sfctx->nothing_changed) { + fprintf(stderr, "Could not change any device features\n"); + free(sfctx); + return nlctx->exit_code ?: 1; + } + if (ret == 0) { + free(sfctx); + return 0; + } + free(sfctx); + return nlctx->exit_code ?: 92; +} diff --git a/netlink/fec.c b/netlink/fec.c new file mode 100644 index 0000000..6027dc0 --- /dev/null +++ b/netlink/fec.c @@ -0,0 +1,360 @@ +/* + * fec.c - netlink implementation of FEC commands + * + * Implementation of "ethtool --show-fec <dev>" and + * "ethtool --set-fec <dev> ..." + */ + +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "bitset.h" +#include "parser.h" + +/* FEC_GET */ + +static void +fec_mode_walk(unsigned int idx, const char *name, bool val, void *data) +{ + bool *empty = data; + + if (!val) + return; + if (empty) + *empty = false; + + /* Rename None to Off - in legacy ioctl None means "not supported" + * rather than supported but disabled. + */ + if (idx == ETHTOOL_LINK_MODE_FEC_NONE_BIT) + name = "Off"; + /* Rename to match the ioctl letter case */ + else if (idx == ETHTOOL_LINK_MODE_FEC_BASER_BIT) + name = "BaseR"; + + print_string(PRINT_ANY, NULL, " %s", name); +} + +static int fec_show_stats(const struct nlattr *nest) +{ + const struct nlattr *tb[ETHTOOL_A_FEC_STAT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + static const struct { + unsigned int attr; + char *name; + } stats[] = { + { ETHTOOL_A_FEC_STAT_CORRECTED, "corrected_blocks" }, + { ETHTOOL_A_FEC_STAT_UNCORR, "uncorrectable_blocks" }, + { ETHTOOL_A_FEC_STAT_CORR_BITS, "corrected_bits" }, + }; + bool header = false; + unsigned int i; + 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++) { + uint64_t *vals; + int lanes, l; + + if (!tb[stats[i].attr] || + !mnl_attr_get_payload_len(tb[stats[i].attr])) + continue; + + if (!header && !is_json_context()) { + printf("Statistics:\n"); + header = true; + } + + if (mnl_attr_get_payload_len(tb[stats[i].attr]) % 8) { + fprintf(stderr, "malformed netlink message (statistic)\n"); + goto err_close_stats; + } + + vals = mnl_attr_get_payload(tb[stats[i].attr]); + lanes = mnl_attr_get_payload_len(tb[stats[i].attr]) / 8 - 1; + + if (!is_json_context()) { + fprintf(stdout, " %s: %" PRIu64 "\n", + stats[i].name, *vals++); + } else { + open_json_object(stats[i].name); + print_u64(PRINT_JSON, "total", NULL, *vals++); + } + + if (lanes) + open_json_array("lanes", ""); + for (l = 0; l < lanes; l++) { + if (!is_json_context()) + fprintf(stdout, " Lane %d: %" PRIu64 "\n", + l, *vals++); + else + print_u64(PRINT_JSON, NULL, NULL, *vals++); + } + if (lanes) + close_json_array(""); + + close_json_object(); + } + close_json_object(); + + return 0; + +err_close_stats: + close_json_object(); + return -1; +} + +int fec_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_FEC_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + const struct stringset *lm_strings; + const char *name; + bool fa, empty; + bool silent; + int err_ret; + u32 active; + 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_FEC_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return err_ret; + lm_strings = global_stringset(ETH_SS_LINK_MODES, nlctx->ethnl2_socket); + + active = 0; + if (tb[ETHTOOL_A_FEC_ACTIVE]) + active = mnl_attr_get_u32(tb[ETHTOOL_A_FEC_ACTIVE]); + + if (silent) + print_nl(); + + open_json_object(NULL); + + print_string(PRINT_ANY, "ifname", "FEC parameters for %s:\n", + nlctx->devname); + + open_json_array("config", "Supported/Configured FEC encodings:"); + fa = tb[ETHTOOL_A_FEC_AUTO] && mnl_attr_get_u8(tb[ETHTOOL_A_FEC_AUTO]); + if (fa) + print_string(PRINT_ANY, NULL, " %s", "Auto"); + empty = !fa; + + ret = walk_bitset(tb[ETHTOOL_A_FEC_MODES], lm_strings, fec_mode_walk, + &empty); + if (ret < 0) + goto err_close_dev; + if (empty) + print_string(PRINT_ANY, NULL, " %s", "None"); + close_json_array("\n"); + + open_json_array("active", "Active FEC encoding:"); + if (active) { + name = get_string(lm_strings, active); + if (name) + /* Take care of renames */ + fec_mode_walk(active, name, true, NULL); + else + print_uint(PRINT_ANY, NULL, " BIT%u", active); + } else { + print_string(PRINT_ANY, NULL, " %s", "None"); + } + close_json_array("\n"); + + if (tb[ETHTOOL_A_FEC_STATS]) { + ret = fec_show_stats(tb[ETHTOOL_A_FEC_STATS]); + if (ret < 0) + goto err_close_dev; + } + + close_json_object(); + + return MNL_CB_OK; + +err_close_dev: + close_json_object(); + return err_ret; +} + +int nl_gfec(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_FEC_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_FEC_GET, + ETHTOOL_A_FEC_HEADER); + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_FEC_GET, + ETHTOOL_A_FEC_HEADER, flags); + if (ret < 0) + return ret; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, fec_reply_cb); + delete_json_obj(); + return ret; +} + +/* FEC_SET */ + +static void strupc(char *dst, const char *src) +{ + while (*src) + *dst++ = toupper(*src++); + *dst = '\0'; +} + +static int fec_parse_bitset(struct nl_context *nlctx, uint16_t type, + const void *data __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) +{ + struct nlattr *bitset_attr; + struct nlattr *bits_attr; + struct nlattr *bit_attr; + char upper[ETH_GSTRING_LEN]; + bool fec_auto = false; + int ret; + + if (!type || dest) { + fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n", + nlctx->cmd, nlctx->param); + return -EFAULT; + } + + bitset_attr = ethnla_nest_start(msgbuff, type); + if (!bitset_attr) + return -EMSGSIZE; + ret = -EMSGSIZE; + if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true)) + goto err; + bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS); + if (!bits_attr) + goto err; + + while (nlctx->argc > 0) { + const char *name = *nlctx->argp; + + if (!strcmp(name, "--")) { + nlctx->argp++; + nlctx->argc--; + break; + } + + if (!strcasecmp(name, "auto")) { + fec_auto = true; + goto next; + } + if (!strcasecmp(name, "off")) { + name = "None"; + } else { + strupc(upper, name); + name = upper; + } + + ret = -EMSGSIZE; + bit_attr = ethnla_nest_start(msgbuff, + ETHTOOL_A_BITSET_BITS_BIT); + if (!bit_attr) + goto err; + if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, name)) + goto err; + ethnla_nest_end(msgbuff, bit_attr); + +next: + nlctx->argp++; + nlctx->argc--; + } + + ethnla_nest_end(msgbuff, bits_attr); + ethnla_nest_end(msgbuff, bitset_attr); + + if (ethnla_put_u8(msgbuff, ETHTOOL_A_FEC_AUTO, fec_auto)) + goto err; + + return 0; +err: + ethnla_nest_cancel(msgbuff, bitset_attr); + return ret; +} + +static const struct param_parser sfec_params[] = { + { + .arg = "encoding", + .type = ETHTOOL_A_FEC_MODES, + .handler = fec_parse_bitset, + .min_argc = 1, + }, + {} +}; + +int nl_sfec(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_FEC_SET, false)) + return -EOPNOTSUPP; + if (!ctx->argc) { + fprintf(stderr, "ethtool (--set-fec): parameters missing\n"); + return 1; + } + + nlctx->cmd = "--set-fec"; + 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_FEC_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEC_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, sfec_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 83; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 83; +} diff --git a/netlink/mm.c b/netlink/mm.c new file mode 100644 index 0000000..d026bc3 --- /dev/null +++ b/netlink/mm.c @@ -0,0 +1,270 @@ +/* + * mm.c - netlink implementation of MAC merge layer settings + * + * Implementation of "ethtool --show-mm <dev>" and "ethtool --set-mm <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" + +/* MM_GET */ + +static const char * +mm_verify_state_to_string(enum ethtool_mm_verify_status state) +{ + switch (state) { + case ETHTOOL_MM_VERIFY_STATUS_INITIAL: + return "INITIAL"; + case ETHTOOL_MM_VERIFY_STATUS_VERIFYING: + return "VERIFYING"; + case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED: + return "SUCCEEDED"; + case ETHTOOL_MM_VERIFY_STATUS_FAILED: + return "FAILED"; + case ETHTOOL_MM_VERIFY_STATUS_DISABLED: + return "DISABLED"; + default: + return "UNKNOWN"; + } +} + +static int show_mm_stats(const struct nlattr *nest) +{ + const struct nlattr *tb[ETHTOOL_A_MM_STAT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + static const struct { + unsigned int attr; + char *name; + } stats[] = { + { ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS, "MACMergeFrameAssErrorCount" }, + { ETHTOOL_A_MM_STAT_SMD_ERRORS, "MACMergeFrameSmdErrorCount" }, + { ETHTOOL_A_MM_STAT_REASSEMBLY_OK, "MACMergeFrameAssOkCount" }, + { ETHTOOL_A_MM_STAT_RX_FRAG_COUNT, "MACMergeFragCountRx" }, + { ETHTOOL_A_MM_STAT_TX_FRAG_COUNT, "MACMergeFragCountTx" }, + { ETHTOOL_A_MM_STAT_HOLD_COUNT, "MACMergeHoldCount" }, + }; + 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[64]; + + 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"); + continue; + } + + 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 mm_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_MM_MAX + 1] = {}; + struct nl_context *nlctx = data; + DECLARE_ATTR_TB_INFO(tb); + 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_MM_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + print_nl(); + + open_json_object(NULL); + + print_string(PRINT_ANY, "ifname", "MAC Merge layer state for %s:\n", + nlctx->devname); + + show_bool("pmac-enabled", "pMAC enabled: %s\n", + tb[ETHTOOL_A_MM_PMAC_ENABLED]); + show_bool("tx-enabled", "TX enabled: %s\n", + tb[ETHTOOL_A_MM_TX_ENABLED]); + show_bool("tx-active", "TX active: %s\n", tb[ETHTOOL_A_MM_TX_ACTIVE]); + show_u32("tx-min-frag-size", "TX minimum fragment size: ", + tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE]); + show_u32("rx-min-frag-size", "RX minimum fragment size: ", + tb[ETHTOOL_A_MM_RX_MIN_FRAG_SIZE]); + show_bool("verify-enabled", "Verify enabled: %s\n", + tb[ETHTOOL_A_MM_VERIFY_ENABLED]); + show_u32("verify-time", "Verify time: ", + tb[ETHTOOL_A_MM_VERIFY_TIME]); + show_u32("max-verify-time", "Max verify time: ", + tb[ETHTOOL_A_MM_MAX_VERIFY_TIME]); + + if (tb[ETHTOOL_A_MM_VERIFY_STATUS]) { + u8 val = mnl_attr_get_u8(tb[ETHTOOL_A_MM_VERIFY_STATUS]); + + print_string(PRINT_ANY, "verify-status", "Verification status: %s\n", + mm_verify_state_to_string(val)); + } + + if (tb[ETHTOOL_A_MM_STATS]) { + ret = show_mm_stats(tb[ETHTOOL_A_MM_STATS]); + if (ret) { + fprintf(stderr, "Failed to print stats: %d\n", ret); + goto err; + } + } + + if (!silent) + print_nl(); + + close_json_object(); + + return MNL_CB_OK; + +err: + close_json_object(); + return err_ret; +} + +int nl_get_mm(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_MM_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_MM_GET, ETHTOOL_A_MM_HEADER); + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_MM_GET, + ETHTOOL_A_MM_HEADER, flags); + if (ret) + return ret; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, mm_reply_cb); + delete_json_obj(); + return ret; +} + +/* MM_SET */ + +static const struct param_parser mm_set_params[] = { + { + .arg = "verify-enabled", + .type = ETHTOOL_A_MM_VERIFY_ENABLED, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "verify-time", + .type = ETHTOOL_A_MM_VERIFY_TIME, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-enabled", + .type = ETHTOOL_A_MM_TX_ENABLED, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "pmac-enabled", + .type = ETHTOOL_A_MM_PMAC_ENABLED, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "tx-min-frag-size", + .type = ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + {} +}; + +int nl_set_mm(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_MM_SET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "--set-mm"; + 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_MM_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret) + return ret; + + if (ethnla_fill_header(msgbuff, ETHTOOL_A_MM_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, mm_set_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret) + return ret; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return ret; + + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret) + return nlctx->exit_code; + + return 0; +} diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c new file mode 100644 index 0000000..49833a2 --- /dev/null +++ b/netlink/module-eeprom.c @@ -0,0 +1,305 @@ +/* + * module-eeprom.c - netlink implementation of module eeprom get command + * + * ethtool -m <dev> + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stddef.h> + +#include "../sff-common.h" +#include "../qsfp.h" +#include "../cmis.h" +#include "../internal.h" +#include "../common.h" +#include "../list.h" +#include "netlink.h" +#include "parser.h" + +#define ETH_I2C_ADDRESS_LOW 0x50 +#define ETH_I2C_MAX_ADDRESS 0x7F + +struct cmd_params { + u8 dump_hex; + u8 dump_raw; + u32 offset; + u32 length; + u32 page; + u32 bank; + u32 i2c_address; +}; + +static const struct param_parser getmodule_params[] = { + { + .arg = "hex", + .handler = nl_parse_u8bool, + .dest_offset = offsetof(struct cmd_params, dump_hex), + .min_argc = 1, + }, + { + .arg = "raw", + .handler = nl_parse_u8bool, + .dest_offset = offsetof(struct cmd_params, dump_raw), + .min_argc = 1, + }, + { + .arg = "offset", + .handler = nl_parse_direct_u32, + .dest_offset = offsetof(struct cmd_params, offset), + .min_argc = 1, + }, + { + .arg = "length", + .handler = nl_parse_direct_u32, + .dest_offset = offsetof(struct cmd_params, length), + .min_argc = 1, + }, + { + .arg = "page", + .handler = nl_parse_direct_u32, + .dest_offset = offsetof(struct cmd_params, page), + .min_argc = 1, + }, + { + .arg = "bank", + .handler = nl_parse_direct_u32, + .dest_offset = offsetof(struct cmd_params, bank), + .min_argc = 1, + }, + { + .arg = "i2c", + .handler = nl_parse_direct_u32, + .dest_offset = offsetof(struct cmd_params, i2c_address), + .min_argc = 1, + }, + {} +}; + +static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list); + +struct eeprom_page_entry { + struct list_head list; /* Member of eeprom_page_list */ + void *data; +}; + +static int eeprom_page_list_add(void *data) +{ + struct eeprom_page_entry *entry; + + entry = malloc(sizeof(*entry)); + if (!entry) + return -ENOMEM; + + entry->data = data; + list_add(&entry->list, &eeprom_page_list); + + return 0; +} + +static void eeprom_page_list_flush(void) +{ + struct eeprom_page_entry *entry; + struct list_head *head, *next; + + list_for_each_safe(head, next, &eeprom_page_list) { + entry = (struct eeprom_page_entry *) head; + free(entry->data); + list_del(head); + free(entry); + } +} + +static int get_eeprom_page_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {}; + struct ethtool_module_eeprom *request = data; + DECLARE_ATTR_TB_INFO(tb); + u8 *eeprom_data; + int ret; + + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + + if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA]) + return MNL_CB_ERROR; + + eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]); + request->data = malloc(request->length); + if (!request->data) + return MNL_CB_ERROR; + memcpy(request->data, eeprom_data, request->length); + + ret = eeprom_page_list_add(request->data); + if (ret < 0) + goto err_list_add; + + return MNL_CB_OK; + +err_list_add: + free(request->data); + return MNL_CB_ERROR; +} + +int nl_get_eeprom_page(struct cmd_context *ctx, + struct ethtool_module_eeprom *request) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsock; + struct nl_msg_buff *msg; + int ret; + + if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS) + return -EINVAL; + + nlsock = nlctx->ethnl_socket; + msg = &nlsock->msgbuff; + + ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET, + ETHTOOL_A_MODULE_EEPROM_HEADER, 0); + if (ret < 0) + return ret; + + if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, + request->length) || + ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, + request->offset) || + ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, + request->page) || + ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, + request->bank) || + ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, + request->i2c_address)) + return -EMSGSIZE; + + ret = nlsock_sendmsg(nlsock, NULL); + if (ret < 0) + return ret; + return nlsock_process_reply(nlsock, get_eeprom_page_reply_cb, + (void *)request); +} + +static int eeprom_dump_hex(struct cmd_context *ctx) +{ + struct ethtool_module_eeprom request = { + .length = 128, + .i2c_address = ETH_I2C_ADDRESS_LOW, + }; + int ret; + + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + + dump_hex(stdout, request.data, request.length, request.offset); + + return 0; +} + +static int eeprom_parse(struct cmd_context *ctx) +{ + struct ethtool_module_eeprom request = { + .length = 1, + .i2c_address = ETH_I2C_ADDRESS_LOW, + }; + int ret; + + /* Fetch the SFF-8024 Identifier Value. For all supported standards, it + * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024, + * revision 4.9. + */ + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + + switch (request.data[0]) { +#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + case SFF8024_ID_SFP: + return sff8079_show_all_nl(ctx); + case SFF8024_ID_QSFP: + case SFF8024_ID_QSFP28: + case SFF8024_ID_QSFP_PLUS: + return sff8636_show_all_nl(ctx); + case SFF8024_ID_QSFP_DD: + case SFF8024_ID_OSFP: + case SFF8024_ID_DSFP: + return cmis_show_all_nl(ctx); +#endif + default: + /* If we cannot recognize the memory map, default to dumping + * the first 128 bytes in hex. + */ + return eeprom_dump_hex(ctx); + } +} + +int nl_getmodule(struct cmd_context *ctx) +{ + struct cmd_params getmodule_cmd_params = {}; + struct ethtool_module_eeprom request = {0}; + struct nl_context *nlctx = ctx->nlctx; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_GET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "-m"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + ret = nl_parser(nlctx, getmodule_params, &getmodule_cmd_params, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return ret; + + if (getmodule_cmd_params.dump_hex && getmodule_cmd_params.dump_raw) { + fprintf(stderr, "Hex and raw dump cannot be specified together\n"); + return -EINVAL; + } + + /* When complete hex/raw dump of the EEPROM is requested, fallback to + * ioctl. Netlink can only request specific pages. + */ + if ((getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) && + !getmodule_cmd_params.page && !getmodule_cmd_params.bank && + !getmodule_cmd_params.i2c_address) { + nlctx->ioctl_fallback = true; + return -EOPNOTSUPP; + } + +#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + if (getmodule_cmd_params.page || getmodule_cmd_params.bank || + getmodule_cmd_params.offset || getmodule_cmd_params.length) +#endif + getmodule_cmd_params.dump_hex = true; + + request.offset = getmodule_cmd_params.offset; + request.length = getmodule_cmd_params.length ?: 128; + request.page = getmodule_cmd_params.page; + request.bank = getmodule_cmd_params.bank; + request.i2c_address = getmodule_cmd_params.i2c_address ?: ETH_I2C_ADDRESS_LOW; + + if (request.page && !request.offset) + request.offset = 128; + + if (getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) { + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + goto cleanup; + + if (getmodule_cmd_params.dump_raw) + fwrite(request.data, 1, request.length, stdout); + else + dump_hex(stdout, request.data, request.length, + request.offset); + } else { + ret = eeprom_parse(ctx); + if (ret < 0) + goto cleanup; + } + +cleanup: + eeprom_page_list_flush(); + return ret; +} diff --git a/netlink/module.c b/netlink/module.c new file mode 100644 index 0000000..54aa6d0 --- /dev/null +++ b/netlink/module.c @@ -0,0 +1,179 @@ +/* + * module.c - netlink implementation of module commands + * + * Implementation of "ethtool --show-module <dev>" and + * "ethtool --set-module <dev> ..." + */ + +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" + +/* MODULE_GET */ + +static const char *module_power_mode_policy_name(u8 val) +{ + switch (val) { + case ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: + return "high"; + case ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO: + return "auto"; + default: + return "unknown"; + } +} + +static const char *module_power_mode_name(u8 val) +{ + switch (val) { + case ETHTOOL_MODULE_POWER_MODE_LOW: + return "low"; + case ETHTOOL_MODULE_POWER_MODE_HIGH: + return "high"; + default: + return "unknown"; + } +} + +int module_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_MODULE_MAX + 1] = {}; + struct nl_context *nlctx = data; + DECLARE_ATTR_TB_INFO(tb); + 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_MODULE_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + print_nl(); + + open_json_object(NULL); + + print_string(PRINT_ANY, "ifname", "Module parameters for %s:\n", + nlctx->devname); + + if (tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]) { + u8 val; + + val = mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]); + print_string(PRINT_ANY, "power-mode-policy", + "power-mode-policy: %s\n", + module_power_mode_policy_name(val)); + } + + if (tb[ETHTOOL_A_MODULE_POWER_MODE]) { + u8 val; + + val = mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE]); + print_string(PRINT_ANY, "power-mode", + "power-mode: %s\n", module_power_mode_name(val)); + } + + close_json_object(); + + return MNL_CB_OK; +} + +int nl_gmodule(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + nlsk = nlctx->ethnl_socket; + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_MODULE_GET, + ETHTOOL_A_MODULE_HEADER, 0); + if (ret < 0) + return ret; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, module_reply_cb); + delete_json_obj(); + return ret; +} + +/* MODULE_SET */ + +static const struct lookup_entry_u8 power_mode_policy_values[] = { + { .arg = "high", .val = ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH }, + { .arg = "auto", .val = ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO }, + {} +}; + +static const struct param_parser smodule_params[] = { + { + .arg = "power-mode-policy", + .type = ETHTOOL_A_MODULE_POWER_MODE_POLICY, + .handler = nl_parse_lookup_u8, + .handler_data = power_mode_policy_values, + .min_argc = 1, + }, + {} +}; + +int nl_smodule(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_MODULE_SET, false)) + return -EOPNOTSUPP; + if (!ctx->argc) { + fprintf(stderr, "ethtool (--set-module): parameters missing\n"); + return 1; + } + + nlctx->cmd = "--set-module"; + 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_MODULE_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_MODULE_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, smodule_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 83; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 83; +} diff --git a/netlink/monitor.c b/netlink/monitor.c new file mode 100644 index 0000000..ace9b25 --- /dev/null +++ b/netlink/monitor.c @@ -0,0 +1,324 @@ +/* + * monitor.c - netlink notification monitor + * + * Implementation of "ethtool --monitor" for watching netlink notifications. + */ + +#include <errno.h> + +#include "../internal.h" +#include "netlink.h" +#include "nlsock.h" +#include "strset.h" + +static struct { + uint8_t cmd; + mnl_cb_t cb; +} monitor_callbacks[] = { + { + .cmd = ETHTOOL_MSG_LINKMODES_NTF, + .cb = linkmodes_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_LINKINFO_NTF, + .cb = linkinfo_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_WOL_NTF, + .cb = wol_reply_cb, + }, + { + .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, + }, + { + .cmd = ETHTOOL_MSG_FEC_NTF, + .cb = fec_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_MODULE_NTF, + .cb = module_reply_cb, + }, +}; + +static void clear_filter(struct nl_context *nlctx) +{ + unsigned int i; + + for (i = 0; i < CMDMASK_WORDS; i++) + nlctx->filter_cmds[i] = 0; +} + +static bool test_filter_cmd(const struct nl_context *nlctx, unsigned int cmd) +{ + return nlctx->filter_cmds[cmd / 32] & (1U << (cmd % 32)); +} + +static void set_filter_cmd(struct nl_context *nlctx, unsigned int cmd) +{ + nlctx->filter_cmds[cmd / 32] |= (1U << (cmd % 32)); +} + +static void set_filter_all(struct nl_context *nlctx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(monitor_callbacks); i++) + set_filter_cmd(nlctx, monitor_callbacks[i].cmd); +} + +static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1); + struct nl_context *nlctx = data; + unsigned int i; + + if (!test_filter_cmd(nlctx, ghdr->cmd)) + return MNL_CB_OK; + + for (i = 0; i < MNL_ARRAY_SIZE(monitor_callbacks); i++) + if (monitor_callbacks[i].cmd == ghdr->cmd) + return monitor_callbacks[i].cb(nlhdr, data); + + return MNL_CB_OK; +} + +struct monitor_option { + const char *pattern; + uint8_t cmd; + uint32_t info_mask; +}; + +static struct monitor_option monitor_opts[] = { + { + .pattern = "|--all", + .cmd = 0, + }, + { + .pattern = "-s|--change", + .cmd = ETHTOOL_MSG_LINKINFO_NTF, + }, + { + .pattern = "-s|--change", + .cmd = ETHTOOL_MSG_LINKMODES_NTF, + }, + { + .pattern = "-s|--change", + .cmd = ETHTOOL_MSG_WOL_NTF, + }, + { + .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, + }, + { + .pattern = "--show-module|--set-module", + .cmd = ETHTOOL_MSG_MODULE_NTF, + }, +}; + +static bool pattern_match(const char *s, const char *pattern) +{ + const char *opt = pattern; + const char *next; + int slen = strlen(s); + int optlen; + + do { + next = opt; + while (*next && *next != '|') + next++; + optlen = next - opt; + if (slen == optlen && !strncmp(s, opt, optlen)) + return true; + + opt = next; + if (*opt == '|') + opt++; + } while (*opt); + + return false; +} + +static int parse_monitor(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + char **argp = ctx->argp; + int argc = ctx->argc; + const char *opt = ""; + bool opt_found; + unsigned int i; + + if (*argp && argp[0][0] == '-') { + opt = *argp; + argp++; + argc--; + } + opt_found = false; + clear_filter(nlctx); + for (i = 0; i < MNL_ARRAY_SIZE(monitor_opts); i++) { + if (pattern_match(opt, monitor_opts[i].pattern)) { + unsigned int cmd = monitor_opts[i].cmd; + + if (!cmd) + set_filter_all(nlctx); + else + set_filter_cmd(nlctx, cmd); + opt_found = true; + } + } + if (!opt_found) { + fprintf(stderr, "monitoring for option '%s' not supported\n", + *argp); + return -1; + } + + if (*argp && strcmp(*argp, WILDCARD_DEVNAME)) + ctx->devname = *argp; + return 0; +} + +int nl_monitor(struct cmd_context *ctx) +{ + 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); + + ret = preload_global_strings(nlsk); + if (ret < 0) + return ret; + ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP, + &grpid, sizeof(grpid)); + if (ret < 0) + return ret; + if (is_dev) { + ret = preload_perdev_strings(nlsk, ctx->devname); + if (ret < 0) + goto out_strings; + } + + nlctx->filter_devname = ctx->devname; + nlctx->is_monitor = true; + nlsk->port = 0; + nlsk->seq = 0; + + fputs("listening...\n", stdout); + fflush(stdout); + ret = nlsock_process_reply(nlsk, monitor_any_cb, nlctx); + +out_strings: + cleanup_all_strings(); + return ret; +} + +void nl_monitor_usage(void) +{ + unsigned int i; + const char *p; + + fputs(" ethtool --monitor Show kernel notifications\n", + stdout); + fputs(" ( [ --all ]", stdout); + for (i = 1; i < MNL_ARRAY_SIZE(monitor_opts); i++) { + if (!strcmp(monitor_opts[i].pattern, monitor_opts[i - 1].pattern)) + continue; + fputs("\n | ", stdout); + for (p = monitor_opts[i].pattern; *p; p++) + if (*p == '|') + fputs(" | ", stdout); + else + fputc(*p, stdout); + } + fputs(" )\n", stdout); + fputs(" [ DEVNAME | * ]\n", stdout); +} diff --git a/netlink/msgbuff.c b/netlink/msgbuff.c new file mode 100644 index 0000000..216f5b9 --- /dev/null +++ b/netlink/msgbuff.c @@ -0,0 +1,256 @@ +/* + * msgbuff.c - netlink message buffer + * + * Data structures and code for flexible message buffer abstraction. + */ + +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> + +#include "../internal.h" +#include "netlink.h" +#include "msgbuff.h" + +#define MAX_MSG_SIZE (4 << 20) /* 4 MB */ + +/** + * msgbuff_realloc() - reallocate buffer if needed + * @msgbuff: message buffer + * @new_size: requested minimum size (add MNL_SOCKET_BUFFER_SIZE if zero) + * + * Make sure allocated buffer has size at least @new_size. If @new_size is + * shorter than current size, do nothing. If @new_size is 0, grow buffer by + * MNL_SOCKET_BUFFER_SIZE. Fail if new size would exceed MAX_MSG_SIZE. + * + * Return: 0 on success or negative error code + */ +int msgbuff_realloc(struct nl_msg_buff *msgbuff, unsigned int new_size) +{ + unsigned int nlhdr_off, genlhdr_off, payload_off; + unsigned int old_size = msgbuff->size; + char *nbuff; + + nlhdr_off = (char *)msgbuff->nlhdr - msgbuff->buff; + genlhdr_off = (char *)msgbuff->genlhdr - msgbuff->buff; + payload_off = (char *)msgbuff->payload - msgbuff->buff; + + if (!new_size) + new_size = old_size + MNL_SOCKET_BUFFER_SIZE; + if (new_size <= old_size) + return 0; + if (new_size > MAX_MSG_SIZE) + return -EMSGSIZE; + nbuff = realloc(msgbuff->buff, new_size); + if (!nbuff) { + msgbuff->buff = NULL; + msgbuff->size = 0; + msgbuff->left = 0; + return -ENOMEM; + } + if (nbuff != msgbuff->buff) { + if (new_size > old_size) + memset(nbuff + old_size, '\0', new_size - old_size); + msgbuff->nlhdr = (struct nlmsghdr *)(nbuff + nlhdr_off); + msgbuff->genlhdr = (struct genlmsghdr *)(nbuff + genlhdr_off); + msgbuff->payload = nbuff + payload_off; + msgbuff->buff = nbuff; + } + msgbuff->size = new_size; + msgbuff->left += (new_size - old_size); + + return 0; +} + +/** + * msgbuff_append() - add contents of another message buffer + * @dest: target message buffer + * @src: source message buffer + * + * Append contents of @src at the end of @dest. Fail if target buffer cannot + * be reallocated to sufficient size. + * + * Return: 0 on success or negative error code. + */ +int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src) +{ + unsigned int src_len = mnl_nlmsg_get_payload_len(src->nlhdr); + 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; + memcpy(mnl_nlmsg_get_payload_tail(dest->nlhdr), src->payload, src_len); + msgbuff_reset(dest, dest_len + src_len); + + return 0; +} + +/** + * ethnla_put - write a netlink attribute to message buffer + * @msgbuff: message buffer + * @type: attribute type + * @len: attribute payload length + * @data: attribute payload + * + * Appends a netlink attribute with header to message buffer, reallocates + * if needed. This is mostly used via specific ethnla_put_* wrappers for + * basic data types. + * + * Return: false on success, true on error (reallocation failed) + */ +bool ethnla_put(struct nl_msg_buff *msgbuff, uint16_t type, size_t len, + const void *data) +{ + struct nlmsghdr *nlhdr = msgbuff->nlhdr; + + while (!mnl_attr_put_check(nlhdr, msgbuff->left, type, len, data)) { + int ret = msgbuff_realloc(msgbuff, 0); + + if (ret < 0) + return true; + } + + return false; +} + +/** + * ethnla_nest_start - start a nested attribute + * @msgbuff: message buffer + * @type: nested attribute type (NLA_F_NESTED is added automatically) + * + * Return: pointer to the nest attribute or null of error + */ +struct nlattr *ethnla_nest_start(struct nl_msg_buff *msgbuff, uint16_t type) +{ + struct nlmsghdr *nlhdr = msgbuff->nlhdr; + struct nlattr *attr; + + do { + attr = mnl_attr_nest_start_check(nlhdr, msgbuff->left, type); + if (attr) + return attr; + } while (msgbuff_realloc(msgbuff, 0) == 0); + + return NULL; +} + +/** + * ethnla_fill_header() - write standard ethtool request header to message + * @msgbuff: message buffer + * @type: attribute type for header nest + * @devname: device name (NULL to omit) + * @flags: request flags (omitted if 0) + * + * Return: pointer to the nest attribute or null of error + */ +bool ethnla_fill_header(struct nl_msg_buff *msgbuff, uint16_t type, + const char *devname, uint32_t flags) +{ + struct nlattr *nest; + + nest = ethnla_nest_start(msgbuff, type); + if (!nest) + return true; + + if ((devname && + ethnla_put_strz(msgbuff, ETHTOOL_A_HEADER_DEV_NAME, devname)) || + (flags && + ethnla_put_u32(msgbuff, ETHTOOL_A_HEADER_FLAGS, flags))) + goto err; + + ethnla_nest_end(msgbuff, nest); + return false; + +err: + ethnla_nest_cancel(msgbuff, nest); + return true; +} + +/** + * __msg_init() - init a genetlink message, fill netlink and genetlink header + * @msgbuff: message buffer + * @family: genetlink family + * @cmd: genetlink command (genlmsghdr::cmd) + * @flags: netlink flags (nlmsghdr::nlmsg_flags) + * @version: genetlink family version (genlmsghdr::version) + * + * Initialize a new genetlink message, fill netlink and genetlink header and + * set pointers in struct nl_msg_buff. + * + * Return: 0 on success or negative error code. + */ +int __msg_init(struct nl_msg_buff *msgbuff, int family, int cmd, + unsigned int flags, int version) +{ + struct nlmsghdr *nlhdr; + struct genlmsghdr *genlhdr; + int ret; + + ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE); + if (ret < 0) + return ret; + memset(msgbuff->buff, '\0', NLMSG_HDRLEN + GENL_HDRLEN); + + nlhdr = mnl_nlmsg_put_header(msgbuff->buff); + nlhdr->nlmsg_type = family; + nlhdr->nlmsg_flags = flags; + msgbuff->nlhdr = nlhdr; + + genlhdr = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*genlhdr)); + genlhdr->cmd = cmd; + genlhdr->version = version; + msgbuff->genlhdr = genlhdr; + + msgbuff->payload = mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN); + + return 0; +} + +/** + * msg_init() - init an ethtool netlink message + * @msgbuff: message buffer + * @cmd: genetlink command (genlmsghdr::cmd) + * @flags: netlink flags (nlmsghdr::nlmsg_flags) + * + * Initialize a new ethtool netlink message, fill netlink and genetlink header + * and set pointers in struct nl_msg_buff. + * + * Return: 0 on success or negative error code. + */ +int msg_init(struct nl_context *nlctx, struct nl_msg_buff *msgbuff, int cmd, + unsigned int flags) +{ + return __msg_init(msgbuff, nlctx->ethnl_fam, cmd, flags, + ETHTOOL_GENL_VERSION); +} + +/** + * msgbuff_init() - initialize a message buffer + * @msgbuff: message buffer + * + * Initialize a message buffer structure before first use. Buffer length is + * set to zero and the buffer is not allocated until the first call to + * msgbuff_reallocate(). + */ +void msgbuff_init(struct nl_msg_buff *msgbuff) +{ + memset(msgbuff, '\0', sizeof(*msgbuff)); +} + +/** + * msg_done() - destroy a message buffer + * @msgbuff: message buffer + * + * Free the buffer and reset size and remaining size. + */ +void msgbuff_done(struct nl_msg_buff *msgbuff) +{ + free(msgbuff->buff); + msgbuff->buff = NULL; + msgbuff->size = 0; + msgbuff->left = 0; +} diff --git a/netlink/msgbuff.h b/netlink/msgbuff.h new file mode 100644 index 0000000..7d6731f --- /dev/null +++ b/netlink/msgbuff.h @@ -0,0 +1,123 @@ +/* + * msgbuff.h - netlink message buffer + * + * Declarations of netlink message buffer and related functions. + */ + +#ifndef ETHTOOL_NETLINK_MSGBUFF_H__ +#define ETHTOOL_NETLINK_MSGBUFF_H__ + +#include <string.h> +#include <libmnl/libmnl.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> + +struct nl_context; + +/** + * struct nl_msg_buff - message buffer abstraction + * @buff: pointer to buffer + * @size: total size of allocated buffer + * @left: remaining length current message end to end of buffer + * @nlhdr: pointer to netlink header of current message + * @genlhdr: pointer to genetlink header of current message + * @payload: pointer to message payload (after genetlink header) + */ +struct nl_msg_buff { + char *buff; + unsigned int size; + unsigned int left; + struct nlmsghdr *nlhdr; + struct genlmsghdr *genlhdr; + void *payload; +}; + +void msgbuff_init(struct nl_msg_buff *msgbuff); +void msgbuff_done(struct nl_msg_buff *msgbuff); +int msgbuff_realloc(struct nl_msg_buff *msgbuff, unsigned int new_size); +int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src); + +int __msg_init(struct nl_msg_buff *msgbuff, int family, int cmd, + unsigned int flags, int version); +int msg_init(struct nl_context *nlctx, struct nl_msg_buff *msgbuff, int cmd, + unsigned int flags); + +bool ethnla_put(struct nl_msg_buff *msgbuff, uint16_t type, size_t len, + const void *data); +struct nlattr *ethnla_nest_start(struct nl_msg_buff *msgbuff, uint16_t type); +bool ethnla_fill_header(struct nl_msg_buff *msgbuff, uint16_t type, + const char *devname, uint32_t flags); + +/* length of current message */ +static inline unsigned int msgbuff_len(const struct nl_msg_buff *msgbuff) +{ + return msgbuff->nlhdr->nlmsg_len; +} + +/* reset message length to position returned by msgbuff_len() */ +static inline void msgbuff_reset(const struct nl_msg_buff *msgbuff, + unsigned int len) +{ + msgbuff->nlhdr->nlmsg_len = len; +} + +/* put data wrappers */ + +static inline void ethnla_nest_end(struct nl_msg_buff *msgbuff, + struct nlattr *nest) +{ + mnl_attr_nest_end(msgbuff->nlhdr, nest); +} + +static inline void ethnla_nest_cancel(struct nl_msg_buff *msgbuff, + struct nlattr *nest) +{ + mnl_attr_nest_cancel(msgbuff->nlhdr, nest); +} + +static inline bool ethnla_put_u32(struct nl_msg_buff *msgbuff, uint16_t type, + uint32_t data) +{ + 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) +{ + return ethnla_put(msgbuff, type, sizeof(uint8_t), &data); +} + +static inline bool ethnla_put_flag(struct nl_msg_buff *msgbuff, uint16_t type, + bool val) +{ + if (val) + return ethnla_put(msgbuff, type, 0, &val); + else + return false; +} + +static inline bool ethnla_put_bitfield32(struct nl_msg_buff *msgbuff, + uint16_t type, uint32_t value, + uint32_t selector) +{ + struct nla_bitfield32 val = { + .value = value, + .selector = selector, + }; + + return ethnla_put(msgbuff, type, sizeof(val), &val); +} + +static inline bool ethnla_put_strz(struct nl_msg_buff *msgbuff, uint16_t type, + const char *data) +{ + return ethnla_put(msgbuff, type, strlen(data) + 1, data); +} + +#endif /* ETHTOOL_NETLINK_MSGBUFF_H__ */ diff --git a/netlink/netlink.c b/netlink/netlink.c new file mode 100644 index 0000000..ef0d825 --- /dev/null +++ b/netlink/netlink.c @@ -0,0 +1,527 @@ +/* + * netlink.c - basic infrastructure for netlink code + * + * Heart of the netlink interface implementation. + */ + +#include <errno.h> + +#include "../internal.h" +#include "netlink.h" +#include "extapi.h" +#include "msgbuff.h" +#include "nlsock.h" +#include "strset.h" + +/* 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 __maybe_unused) +{ + const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1); + + fprintf(stderr, "received unexpected message: len=%u type=%u cmd=%u\n", + nlhdr->nlmsg_len, nlhdr->nlmsg_type, ghdr->cmd); + return MNL_CB_OK; +} + +/* standard attribute parser callback; it fills provided array with pointers + * to attributes like kernel nla_parse(). We must expect to run on top of + * a newer kernel which may send attributes that we do not know (yet). Rather + * than treating them as an error, just ignore them. + */ +int attr_cb(const struct nlattr *attr, void *data) +{ + const struct attr_tb_info *tb_info = data; + uint16_t type = mnl_attr_get_type(attr); + + if (type <= tb_info->max_type) + tb_info->tb[type] = attr; + + return MNL_CB_OK; +} + +/* misc helpers */ + +const char *get_dev_name(const struct nlattr *nest) +{ + const struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + int ret; + + if (!nest) + return NULL; + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + if (ret < 0 || !tb[ETHTOOL_A_HEADER_DEV_NAME]) + return "(none)"; + return mnl_attr_get_str(tb[ETHTOOL_A_HEADER_DEV_NAME]); +} + +int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname) +{ + const struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1] = {}; + const struct nlattr *index_attr; + const struct nlattr *name_attr; + DECLARE_ATTR_TB_INFO(tb); + int ret; + + if (ifindex) + *ifindex = 0; + if (ifname) + memset(ifname, '\0', ALTIFNAMSIZ); + + if (!nest) + return -EFAULT; + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + index_attr = tb[ETHTOOL_A_HEADER_DEV_INDEX]; + name_attr = tb[ETHTOOL_A_HEADER_DEV_NAME]; + if (ret < 0 || (ifindex && !index_attr) || (ifname && !name_attr)) + return -EFAULT; + + if (ifindex) + *ifindex = mnl_attr_get_u32(index_attr); + if (ifname) { + strncpy(ifname, mnl_attr_get_str(name_attr), ALTIFNAMSIZ); + if (ifname[ALTIFNAMSIZ - 1]) { + ifname[ALTIFNAMSIZ - 1] = '\0'; + fprintf(stderr, "kernel device name too long: '%s'\n", + mnl_attr_get_str(name_attr)); + return -EFAULT; + } + } + return 0; +} + +/** + * 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; + + 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 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); + struct nlattr *grp_attr; + int ret; + + mnl_attr_for_each_nested(grp_attr, nest) { + ret = mnl_attr_parse_nested(grp_attr, attr_cb, &grp_tb_info); + if (ret < 0) + return; + if (!grp_tb[CTRL_ATTR_MCAST_GRP_NAME] || + !grp_tb[CTRL_ATTR_MCAST_GRP_ID]) + continue; + if (strcmp(mnl_attr_get_str(grp_tb[CTRL_ATTR_MCAST_GRP_NAME]), + ETHTOOL_MCGRP_MONITOR_NAME)) + continue; + nlctx->ethnl_mongrp = + mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]); + return; + } +} + +static int __maybe_unused family_info_cb(const struct nlmsghdr *nlhdr, + void *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: + 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(nlctx, attr); + break; + } + } + + return MNL_CB_OK; +} + +#ifdef TEST_ETHTOOL +static int get_genl_family(struct nl_context *nlctx __maybe_unused, + struct nl_socket *nlsk __maybe_unused) +{ + return 0; +} +#else +static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk) +{ + struct nl_msg_buff *msgbuff = &nlsk->msgbuff; + int ret; + + 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, ETHTOOL_GENL_NAME)) + goto out; + + nlsock_sendmsg(nlsk, NULL); + nlsock_process_reply(nlsk, family_info_cb, nlctx); + ret = nlctx->ethnl_fam ? 0 : -EADDRNOTAVAIL; + +out: + nlctx->suppress_nlerr = 0; + return ret; +} +#endif + +int netlink_init(struct cmd_context *ctx) +{ + struct nl_context *nlctx; + int ret; + + nlctx = calloc(1, sizeof(*nlctx)); + if (!nlctx) + return -ENOMEM; + nlctx->ctx = ctx; + ret = nlsock_init(nlctx, &nlctx->ethnl_socket, NETLINK_GENERIC); + if (ret < 0) + goto out_free; + ret = get_genl_family(nlctx, nlctx->ethnl_socket); + if (ret < 0) + goto out_nlsk; + + ctx->nlctx = nlctx; + return 0; + +out_nlsk: + nlsock_done(nlctx->ethnl_socket); +out_free: + free(nlctx->ops_info); + free(nlctx); + return ret; +} + +static void netlink_done(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + + if (!nlctx) + return; + + 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 + * @nlchk: netlink capability check + * @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_chk_t nlchk, + 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 (nlchk && !nlchk(ctx)) { + reason = "ioctl-only request"; + goto no_support; + } + 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 new file mode 100644 index 0000000..1274a3b --- /dev/null +++ b/netlink/netlink.h @@ -0,0 +1,178 @@ +/* + * netlink.h - common interface for all netlink code + * + * Declarations of data structures, global data and helpers for netlink code + */ + +#ifndef ETHTOOL_NETLINK_INT_H__ +#define ETHTOOL_NETLINK_INT_H__ + +#include <libmnl/libmnl.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#include <linux/ethtool_netlink.h> +#include "nlsock.h" + +#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; + const char *devname; + bool is_dump; + int exit_code; + 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; + bool is_monitor; + uint32_t filter_cmds[CMDMASK_WORDS]; + const char *filter_devname; + bool no_banner; + const char *cmd; + const char *param; + char **argp; + unsigned int argc; + bool ioctl_fallback; + bool wildcard_unsupported; +}; + +struct attr_tb_info { + const struct nlattr **tb; + unsigned int max_type; +}; + +#define DECLARE_ATTR_TB_INFO(tbl) \ + struct attr_tb_info tbl ## _info = { (tbl), (MNL_ARRAY_SIZE(tbl) - 1) } + +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); +int fec_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int module_reply_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 char *key, + const char *fmt, + const struct nlattr *attr) +{ + if (is_json_context()) { + if (attr) + print_uint(PRINT_JSON, key, NULL, + mnl_attr_get_u32(attr)); + } else { + if (attr) + printf("%s%u\n", fmt, mnl_attr_get_u32(attr)); + else + printf("%sn/a\n", fmt); + } +} + +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); +} + +static inline void show_cr(void) +{ + if (!is_json_context()) + putchar('\n'); +} + +/* misc */ + +static inline void copy_devname(char *dst, const char *src) +{ + strncpy(dst, src, ALTIFNAMSIZ); + dst[ALTIFNAMSIZ - 1] = '\0'; +} + +static inline bool dev_ok(const struct nl_context *nlctx) +{ + return !nlctx->filter_devname || + (nlctx->devname && + !strcmp(nlctx->devname, nlctx->filter_devname)); +} + +static inline int netlink_init_ethnl2_socket(struct nl_context *nlctx) +{ + if (nlctx->ethnl2_socket) + return 0; + return nlsock_init(nlctx, &nlctx->ethnl2_socket, NETLINK_GENERIC); +} + +static inline int netlink_init_rtnl_socket(struct nl_context *nlctx) +{ + if (nlctx->rtnl_socket) + return 0; + return nlsock_init(nlctx, &nlctx->rtnl_socket, NETLINK_ROUTE); +} + +#endif /* ETHTOOL_NETLINK_INT_H__ */ diff --git a/netlink/nlsock.c b/netlink/nlsock.c new file mode 100644 index 0000000..0ec2738 --- /dev/null +++ b/netlink/nlsock.c @@ -0,0 +1,405 @@ +/* + * nlsock.c - netlink socket + * + * Data structure and code for netlink socket abstraction. + */ + +#include <stdint.h> +#include <errno.h> + +#include "../internal.h" +#include "nlsock.h" +#include "netlink.h" +#include "prettymsg.h" + +#define NLSOCK_RECV_BUFFSIZE 65536 + +static void ctrl_msg_summary(const struct nlmsghdr *nlhdr) +{ + const struct nlmsgerr *nlerr; + + switch (nlhdr->nlmsg_type) { + case NLMSG_NOOP: + printf(" noop\n"); + break; + case NLMSG_ERROR: + printf(" error"); + if (nlhdr->nlmsg_len < NLMSG_HDRLEN + sizeof(*nlerr)) { + printf(" malformed\n"); + break; + } + nlerr = mnl_nlmsg_get_payload(nlhdr); + printf(" errno=%d\n", nlerr->error); + break; + case NLMSG_DONE: + printf(" done\n"); + break; + case NLMSG_OVERRUN: + printf(" overrun\n"); + break; + default: + printf(" type %u\n", nlhdr->nlmsg_type); + break; + } +} + +static void genl_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam, + bool outgoing, bool pretty) +{ + if (nlhdr->nlmsg_type == ethnl_fam) { + const struct pretty_nlmsg_desc *msg_desc; + const struct genlmsghdr *ghdr; + unsigned int n_desc; + + printf(" ethool"); + if (nlhdr->nlmsg_len < NLMSG_HDRLEN + GENL_HDRLEN) { + printf(" malformed\n"); + return; + } + ghdr = mnl_nlmsg_get_payload(nlhdr); + + msg_desc = outgoing ? ethnl_umsg_desc : ethnl_kmsg_desc; + n_desc = outgoing ? ethnl_umsg_n_desc : ethnl_kmsg_n_desc; + if (ghdr->cmd < n_desc && msg_desc[ghdr->cmd].name) + printf(" %s", msg_desc[ghdr->cmd].name); + else + printf(" cmd %u", ghdr->cmd); + fputc('\n', stdout); + + if (pretty) + pretty_print_genlmsg(nlhdr, msg_desc, n_desc, 0); + return; + } + + if (nlhdr->nlmsg_type == GENL_ID_CTRL) { + printf(" genl-ctrl\n"); + if (pretty) + pretty_print_genlmsg(nlhdr, genlctrl_msg_desc, + genlctrl_msg_n_desc, 0); + } else { + fputc('\n', stdout); + if (pretty) + pretty_print_genlmsg(nlhdr, NULL, 0, 0); + } +} + +static void rtnl_msg_summary(const struct nlmsghdr *nlhdr, bool pretty) +{ + unsigned int type = nlhdr->nlmsg_type; + + if (type < rtnl_msg_n_desc && rtnl_msg_desc[type].name) + printf(" %s\n", rtnl_msg_desc[type].name); + else + printf(" type %u\n", type); + + if (pretty) + pretty_print_rtnlmsg(nlhdr, 0); +} + +static void debug_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam, + int nl_fam, bool outgoing, bool pretty) +{ + printf(" msg length %u", nlhdr->nlmsg_len); + + if (nlhdr->nlmsg_type < NLMSG_MIN_TYPE) { + ctrl_msg_summary(nlhdr); + return; + } + + switch(nl_fam) { + case NETLINK_GENERIC: + genl_msg_summary(nlhdr, ethnl_fam, outgoing, pretty); + break; + case NETLINK_ROUTE: + rtnl_msg_summary(nlhdr, pretty); + break; + default: + fputc('\n', stdout); + break; + } +} + +static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len, + bool outgoing) +{ + const char *dirlabel = outgoing ? "sending" : "received"; + uint32_t debug = nlsk->nlctx->ctx->debug; + const struct nlmsghdr *nlhdr = msg; + bool summary, dump, pretty; + const char *nl_fam_label; + int left = len; + + summary = debug_on(debug, DEBUG_NL_MSGS); + dump = debug_on(debug, + outgoing ? DEBUG_NL_DUMP_SND : DEBUG_NL_DUMP_RCV); + pretty = debug_on(debug, DEBUG_NL_PRETTY_MSG); + if (!summary && !dump) + return; + switch(nlsk->nl_fam) { + case NETLINK_GENERIC: + nl_fam_label = "genetlink"; + break; + case NETLINK_ROUTE: + nl_fam_label = "rtnetlink"; + break; + default: + nl_fam_label = "netlink"; + break; + } + printf("%s %s packet (%u bytes):\n", dirlabel, nl_fam_label, len); + + while (nlhdr && left > 0 && mnl_nlmsg_ok(nlhdr, left)) { + if (summary) + debug_msg_summary(nlhdr, nlsk->nlctx->ethnl_fam, + nlsk->nl_fam, outgoing, pretty); + if (dump) + mnl_nlmsg_fprintf(stdout, nlhdr, nlhdr->nlmsg_len, + GENL_HDRLEN); + + nlhdr = mnl_nlmsg_next(nlhdr, &left); + } +} + +/** + * nlsock_process_ack() - process NLMSG_ERROR message from kernel + * @nlhdr: pointer to netlink header + * @len: length of received data (from nlhdr to end of buffer) + * @suppress_nlerr: 0 show all errors, 1 silence -EOPNOTSUPP, 2 silence all + * + * Return: error code extracted from the message + */ +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)) || (len < nlhdr->nlmsg_len)) + return -EFAULT; + nlerr = mnl_nlmsg_get_payload(nlhdr); + 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 tlv_done; + + if (tb[NLMSGERR_ATTR_MSG]) { + const char *msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]); + + fprintf(stderr, "netlink %s: %s", + nlerr->error ? "error" : "warning", msg); + if (!pretty && tb[NLMSGERR_ATTR_OFFS]) + fprintf(stderr, " (offset %u)", + 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]); + +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); + } + return nlerr->error; +} + +/** + * nlsock_process_reply() - process reply packet(s) from kernel + * @nlsk: netlink socket to read from + * @reply_cb: callback to process each message + * @data: pointer passed as argument to @reply_cb callback + * + * Read packets from kernel and pass reply messages to @reply_cb callback + * until an error is encountered or NLMSG_ERR message is received. In the + * latter case, return value is the error code extracted from it. + * + * Return: 0 on success or negative error code + */ +int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data) +{ + struct nl_msg_buff *msgbuff = &nlsk->msgbuff; + struct nlmsghdr *nlhdr; + ssize_t len; + char *buff; + int ret; + + ret = msgbuff_realloc(msgbuff, NLSOCK_RECV_BUFFSIZE); + if (ret < 0) + return ret; + buff = msgbuff->buff; + + do { + len = mnl_socket_recvfrom(nlsk->sk, buff, msgbuff->size); + if (len <= 0) + return (len ? -EFAULT : 0); + debug_msg(nlsk, buff, len, false); + if (len < NLMSG_HDRLEN) + return -EFAULT; + + nlhdr = (struct nlmsghdr *)buff; + if (nlhdr->nlmsg_type == NLMSG_ERROR) { + 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, suppress, pretty); + } + + msgbuff->nlhdr = nlhdr; + msgbuff->genlhdr = mnl_nlmsg_get_payload(nlhdr); + msgbuff->payload = + mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN); + ret = mnl_cb_run(buff, len, nlsk->seq, nlsk->port, reply_cb, + data); + } while (ret > 0); + + return ret; +} + +int nlsock_prep_get_request(struct nl_socket *nlsk, unsigned int nlcmd, + uint16_t hdr_attrtype, u32 flags) +{ + unsigned int nlm_flags = NLM_F_REQUEST | NLM_F_ACK; + struct nl_context *nlctx = nlsk->nlctx; + const char *devname = nlctx->ctx->devname; + int ret; + + if (devname && !strcmp(devname, WILDCARD_DEVNAME)) { + devname = NULL; + nlm_flags |= NLM_F_DUMP; + } + nlctx->is_dump = !devname; + + ret = msg_init(nlctx, &nlsk->msgbuff, nlcmd, nlm_flags); + if (ret < 0) + return ret; + if (ethnla_fill_header(&nlsk->msgbuff, hdr_attrtype, devname, flags)) + return -EMSGSIZE; + + return 0; +} + +#ifndef TEST_ETHTOOL +/** + * nlsock_sendmsg() - send a netlink message to kernel + * @nlsk: netlink socket + * @altbuff: alternative message buffer; if null, use buffer embedded in @nlsk + * + * Return: sent size or negative error code + */ +ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *altbuff) +{ + struct nl_msg_buff *msgbuff = altbuff ?: &nlsk->msgbuff; + struct nlmsghdr *nlhdr = msgbuff->nlhdr; + + nlhdr->nlmsg_seq = ++nlsk->seq; + debug_msg(nlsk, msgbuff->buff, nlhdr->nlmsg_len, true); + return mnl_socket_sendto(nlsk->sk, nlhdr, nlhdr->nlmsg_len); +} +#endif + +/** + * nlsock_send_get_request() - send request and process reply + * @nlsk: netlink socket + * @cb: callback to process reply message(s) + * + * This simple helper only handles the most common case when the embedded + * message buffer is sent and @cb takes netlink context (struct nl_context) + * as last argument. + */ +int nlsock_send_get_request(struct nl_socket *nlsk, mnl_cb_t cb) +{ + int ret; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + goto err; + ret = nlsock_process_reply(nlsk, cb, nlsk->nlctx); + if (ret == 0) + return 0; +err: + return nlsk->nlctx->exit_code ?: 1; +} + +/** + * nlsock_init() - allocate and initialize netlink socket + * @nlctx: netlink context + * @__nlsk: store pointer to the allocated socket here + * @nl_fam: netlink family (e.g. NETLINK_GENERIC or NETLINK_ROUTE) + * + * Allocate and initialize netlink socket and its embedded message buffer. + * Cleans up on error, caller is responsible for destroying the socket with + * nlsock_done() on success. + * + * Return: 0 on success or negative error code + */ +int nlsock_init(struct nl_context *nlctx, struct nl_socket **__nlsk, int nl_fam) +{ + struct nl_socket *nlsk; + int val; + int ret; + + nlsk = calloc(1, sizeof(*nlsk)); + if (!nlsk) + return -ENOMEM; + nlsk->nlctx = nlctx; + msgbuff_init(&nlsk->msgbuff); + + ret = -ECONNREFUSED; + nlsk->sk = mnl_socket_open(nl_fam); + if (!nlsk->sk) + goto out_msgbuff; + val = 1; + mnl_socket_setsockopt(nlsk->sk, NETLINK_EXT_ACK, &val, sizeof(val)); + ret = mnl_socket_bind(nlsk->sk, 0, MNL_SOCKET_AUTOPID); + if (ret < 0) + goto out_close; + nlsk->port = mnl_socket_get_portid(nlsk->sk); + nlsk->nl_fam = nl_fam; + + *__nlsk = nlsk; + return 0; + +out_close: + if (nlsk->sk) + mnl_socket_close(nlsk->sk); +out_msgbuff: + msgbuff_done(&nlsk->msgbuff); + free(nlsk); + return ret; +} + +/** + * nlsock_done() - destroy a netlink socket + * @nlsk: netlink socket + * + * Close the socket and free the structure and related data. + */ +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/nlsock.h b/netlink/nlsock.h new file mode 100644 index 0000000..b015f86 --- /dev/null +++ b/netlink/nlsock.h @@ -0,0 +1,45 @@ +/* + * nlsock.h - netlink socket + * + * Declarations of netlink socket structure and related functions. + */ + +#ifndef ETHTOOL_NETLINK_NLSOCK_H__ +#define ETHTOOL_NETLINK_NLSOCK_H__ + +#include <libmnl/libmnl.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#include <linux/ethtool_netlink.h> +#include "msgbuff.h" + +struct nl_context; + +/** + * struct nl_socket - netlink socket abstraction + * @nlctx: netlink context + * @sk: libmnl socket handle + * @msgbuff: embedded message buffer used by default + * @port: port number for netlink header + * @seq: autoincremented sequence number for netlink header + * @nl_fam: netlink family (e.g. NETLINK_GENERIC or NETLINK_ROUTE) + */ +struct nl_socket { + struct nl_context *nlctx; + struct mnl_socket *sk; + struct nl_msg_buff msgbuff; + unsigned int port; + unsigned int seq; + int nl_fam; +}; + +int nlsock_init(struct nl_context *nlctx, struct nl_socket **__nlsk, + int nl_fam); +void nlsock_done(struct nl_socket *nlsk); +int nlsock_prep_get_request(struct nl_socket *nlsk, unsigned int nlcmd, + uint16_t hdr_attrtype, u32 flags); +ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *__msgbuff); +int nlsock_send_get_request(struct nl_socket *nlsk, mnl_cb_t cb); +int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data); + +#endif /* ETHTOOL_NETLINK_NLSOCK_H__ */ diff --git a/netlink/parser.c b/netlink/parser.c new file mode 100644 index 0000000..6f86361 --- /dev/null +++ b/netlink/parser.c @@ -0,0 +1,1141 @@ +/* + * parser.c - netlink command line parser + * + * Implementation of command line parser used by netlink code. + */ + +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" + +static void parser_err_unknown_param(struct nl_context *nlctx) +{ + fprintf(stderr, "ethtool (%s): unknown parameter '%s'\n", nlctx->cmd, + nlctx->param); +} + +static void parser_err_dup_param(struct nl_context *nlctx) +{ + fprintf(stderr, "ethtool (%s): duplicate parameter '%s'\n", nlctx->cmd, + nlctx->param); +} + +static void parser_err_min_argc(struct nl_context *nlctx, unsigned int min_argc) +{ + if (min_argc == 1) + fprintf(stderr, "ethtool (%s): no value for parameter '%s'\n", + nlctx->cmd, nlctx->param); + else + fprintf(stderr, + "ethtool (%s): parameter '%s' requires %u words\n", + nlctx->cmd, nlctx->param, min_argc); +} + +static void parser_err_invalid_value(struct nl_context *nlctx, const char *val) +{ + fprintf(stderr, "ethtool (%s): invalid value '%s' for parameter '%s'\n", + nlctx->cmd, val, nlctx->param); +} + +static void parser_err_invalid_flag(struct nl_context *nlctx, const char *flag) +{ + fprintf(stderr, "ethtool (%s): flag '%s' for parameter '%s' is not followed by 'on' or 'off'\n", + nlctx->cmd, flag, nlctx->param); +} + +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) +{ + unsigned long long val; + char *endptr; + + if (!arg || !arg[0]) + return -EINVAL; + val = strtoul(arg, &endptr, base); + if (*endptr || val < min || val > max) + return -EINVAL; + + *result = (uint32_t)val; + return 0; +} + +static int parse_u32d(const char *arg, uint32_t *result) +{ + return __parse_u32(arg, result, 0, 0xffffffff, 10); +} + +static int parse_x32(const char *arg, uint32_t *result) +{ + return __parse_u32(arg, result, 0, 0xffffffff, 16); +} + +int parse_u32(const char *arg, uint32_t *result) +{ + if (!arg) + return -EINVAL; + if (__prefix_0x(arg)) + return parse_x32(arg + 2, result); + else + return parse_u32d(arg, result); +} + +static int parse_u8(const char *arg, uint8_t *result) +{ + uint32_t val; + int ret = parse_u32(arg, &val); + + if (ret < 0) + return ret; + if (val > UINT8_MAX) + return -EINVAL; + + *result = (uint8_t)val; + return 0; +} + +static int lookup_u32(const char *arg, uint32_t *result, + const struct lookup_entry_u32 *tbl) +{ + if (!arg) + return -EINVAL; + while (tbl->arg) { + if (!strcmp(tbl->arg, arg)) { + *result = tbl->val; + return 0; + } + tbl++; + } + + return -EINVAL; +} + +static int lookup_u8(const char *arg, uint8_t *result, + const struct lookup_entry_u8 *tbl) +{ + if (!arg) + return -EINVAL; + while (tbl->arg) { + if (!strcmp(tbl->arg, arg)) { + *result = tbl->val; + return 0; + } + tbl++; + } + + return -EINVAL; +} + +/* 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 __maybe_unused, uint16_t type, + const void *data __maybe_unused, struct nl_msg_buff *msgbuff, + void *dest) +{ + if (dest) + *(bool *)dest = true; + return (type && ethnla_put_flag(msgbuff, type, true)) ? -EMSGSIZE : 0; +} + +/* 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 __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) +{ + const char *arg = *nlctx->argp; + + nlctx->argp++; + nlctx->argc--; + + if (dest) + *(const char **)dest = arg; + return (type && ethnla_put_strz(msgbuff, type, arg)) ? -EMSGSIZE : 0; +} + +/* Parser handler for unsigned 32-bit integer. Expects a numeric argument + * (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 __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) +{ + const char *arg = *nlctx->argp; + uint32_t val; + int ret; + + nlctx->argp++; + nlctx->argc--; + ret = parse_u32(arg, &val); + if (ret < 0) { + parser_err_invalid_value(nlctx, arg); + return ret; + } + + if (dest) + *(uint32_t *)dest = val; + return (type && ethnla_put_u32(msgbuff, type, val)) ? -EMSGSIZE : 0; +} + +/* Parser handler for unsigned 32-bit integer. Expects a numeric argument + * (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 __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) +{ + const char *arg = *nlctx->argp; + uint8_t val; + int ret; + + nlctx->argp++; + nlctx->argc--; + ret = parse_u8(arg, &val); + if (ret < 0) { + parser_err_invalid_value(nlctx, arg); + return ret; + } + + if (dest) + *(uint8_t *)dest = val; + 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 = 0.0; + 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 __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) +{ + const char *arg = *nlctx->argp; + int ret; + + nlctx->argp++; + nlctx->argc--; + if (!strcmp(arg, "on")) { + if (dest) + *(uint8_t *)dest = 1; + ret = type ? ethnla_put_u8(msgbuff, type, 1) : 0; + } else if (!strcmp(arg, "off")) { + if (dest) + *(uint8_t *)dest = 0; + ret = type ? ethnla_put_u8(msgbuff, type, 0) : 0; + } else { + parser_err_invalid_value(nlctx, arg); + return -EINVAL; + } + + return ret ? -EMSGSIZE : 0; +} + +/* Parser handler for 32-bit lookup value. Expects a string argument, looks it + * up in a table, generates NLA_U32 or fills uint32_t variable. The @data + * parameter is a null terminated array of struct lookup_entry_u32. + */ +int nl_parse_lookup_u32(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest) +{ + const char *arg = *nlctx->argp; + uint32_t val; + int ret; + + nlctx->argp++; + nlctx->argc--; + ret = lookup_u32(arg, &val, data); + if (ret < 0) { + parser_err_invalid_value(nlctx, arg); + return ret; + } + + if (dest) + *(uint32_t *)dest = val; + return (type && ethnla_put_u32(msgbuff, type, val)) ? -EMSGSIZE : 0; +} + +/* Parser handler for 8-bit lookup value. Expects a string argument, looks it + * up in a table, generates NLA_U8 or fills uint8_t variable. The @data + * parameter is a null terminated array of struct lookup_entry_u8. + */ +int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest) +{ + const char *arg = *nlctx->argp; + uint8_t val; + int ret; + + nlctx->argp++; + nlctx->argc--; + ret = lookup_u8(arg, &val, data); + if (ret < 0) { + parser_err_invalid_value(nlctx, arg); + return ret; + } + + if (dest) + *(uint8_t *)dest = val; + return (type && ethnla_put_u8(msgbuff, type, val)) ? -EMSGSIZE : 0; +} + +/* number of significant bits */ +static unsigned int __nsb(uint32_t x) +{ + unsigned int ret = 0; + + if (x & 0xffff0000U) { + x >>= 16; + ret += 16; + } + if (x & 0xff00U) { + x >>= 8; + ret += 8; + } + if (x & 0xf0U) { + x >>= 4; + ret += 4; + } + if (x & 0xcU) { + x >>= 2; + ret += 2; + } + if (x & 0x2U) { + x >>= 1; + ret += 1; + } + + return ret + x; +} + +static bool __is_hex(char c) +{ + if (isdigit(c)) + return true; + else + return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +static unsigned int __hex_val(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 0xa; + if (c >= 'A' && c <= 'F') + return c - 'A' + 0xa; + return 0; +} + +static bool __bytestr_delim(const char *p, char delim) +{ + return !*p || (delim ? (*p == delim) : !__is_hex(*p)); +} + +/* Parser handler for generic byte string in MAC-like format. Expects string + * argument in the "[[:xdigit:]]{2}(:[[:xdigit:]]{2})*" format, generates + * NLA_BINARY or fills a struct byte_str_value (if @dest is not null and the + * handler succeeds, caller is responsible for freeing the value). The @data + * parameter points to struct byte_str_parser_data. + */ +int nl_parse_byte_str(struct nl_context *nlctx, uint16_t type, const void *data, + struct nl_msg_buff *msgbuff, void *dest) +{ + const struct byte_str_parser_data *pdata = data; + struct byte_str_value *dest_value = dest; + const char *arg = *nlctx->argp; + uint8_t *val = NULL; + unsigned int len, i; + const char *p; + int ret; + + nlctx->argp++; + nlctx->argc--; + + len = 0; + p = arg; + if (!*p) + goto err; + while (true) { + len++; + if (!__bytestr_delim(p, pdata->delim)) + p++; + if (!__bytestr_delim(p, pdata->delim)) + p++; + if (!__bytestr_delim(p, pdata->delim)) + goto err; + if (!*p) + break; + p++; + if (*p && __bytestr_delim(p, pdata->delim)) + goto err; + } + if (len < pdata->min_len || (pdata->max_len && len > pdata->max_len)) + goto err; + val = malloc(len); + if (!val) + return -ENOMEM; + + p = arg; + for (i = 0; i < len; i++) { + uint8_t byte = 0; + + if (!__is_hex(*p)) + goto err; + while (__is_hex(*p)) + byte = 16 * byte + __hex_val(*p++); + if (!__bytestr_delim(p, pdata->delim)) + goto err; + val[i] = byte; + if (*p) + p++; + } + ret = type ? ethnla_put(msgbuff, type, len, val) : 0; + if (dest) { + dest_value->len = len; + dest_value->data = val; + } else { + free(val); + } + return ret; + +err: + free(val); + fprintf(stderr, "ethtool (%s): invalid value '%s' of parameter '%s'\n", + nlctx->cmd, arg, nlctx->param); + return -EINVAL; +} + +/* Parser handler for parameters recognized for backward compatibility but + * supposed to fail without passing to kernel. Does not generate any netlink + * attributes of fill any variable. The @data parameter points to struct + * 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 __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; + + fprintf(stderr, "ethtool (%s): ", nlctx->cmd); + fprintf(stderr, parser_data->err_msg, nlctx->param); + if (nlctx->argc < skip) { + fprintf(stderr, "ethtool (%s): too few arguments for parameter '%s' (expected %u)\n", + nlctx->cmd, nlctx->param, skip); + } else { + nlctx->argp += skip; + nlctx->argc -= skip; + } + + return parser_data->ret_val; +} + +/* bitset parser handlers */ + +/* Return true if a bitset argument should be parsed as numeric, i.e. + * (a) it starts with '0x' + * (b) it consists only of hex digits and at most one slash which can be + * optionally followed by "0x"; if no_mask is true, slash is not allowed + */ +static bool is_numeric_bitset(const char *arg, bool no_mask) +{ + const char *p = arg; + bool has_slash = false; + + if (!arg) + return false; + if (__prefix_0x(arg)) + return true; + while (*p) { + if (*p == '/') { + if (has_slash || no_mask) + return false; + has_slash = true; + p++; + if (__prefix_0x(p)) + p += 2; + continue; + } + if (!__is_hex(*p)) + return false; + p++; + } + return true; +} + +#define __MAX_U32_DIGITS 10 + +/* Parse hex string (without leading "0x") into a bitmap consisting of 32-bit + * words. Caller must make sure arg is at least len characters long and dst has + * place for at least (len + 7) / 8 32-bit words. If force_hex is false, allow + * also base 10 unsigned 32-bit value. + * + * Returns number of significant bits in the bitmap on success and negative + * value on error. + */ +static int __parse_num_string(const char *arg, unsigned int len, uint32_t *dst, + bool force_hex) +{ + char buff[__MAX_U32_DIGITS + 1] = {}; + unsigned int nbits = 0; + const char *p = arg; + + if (!len) + return -EINVAL; + if (!force_hex && len <= __MAX_U32_DIGITS) { + strncpy(buff, arg, len); + if (!buff[__MAX_U32_DIGITS]) { + u32 val; + int ret; + + ret = parse_u32d(buff, &val); + if (!ret) { + *dst = val; + return __nsb(val); + } + } + } + + dst += (len - 1) / 8; + while (len > 0) { + unsigned int chunk = (len % 8) ?: 8; + unsigned long val; + char *endp; + + memcpy(buff, p, chunk); + buff[chunk] = '\0'; + val = strtoul(buff, &endp, 16); + if (*endp) + return -EINVAL; + *dst-- = (uint32_t)val; + if (nbits) + nbits += 4 * chunk; + else + nbits = __nsb(val); + + p += chunk; + len -= chunk; + } + return nbits; +} + +/* Parse bitset provided as a base 16 numeric value (@no_mask is true) or pair + * of base 16 numeric values separated by '/' (@no_mask is false). The "0x" + * prefix is optional. Generates bitset nested attribute in compact form. + */ +static int parse_numeric_bitset(struct nl_context *nlctx, uint16_t type, + bool no_mask, bool force_hex, + struct nl_msg_buff *msgbuff) +{ + unsigned int nwords, len1, len2; + const char *arg = *nlctx->argp; + bool force_hex1 = force_hex; + bool force_hex2 = force_hex; + uint32_t *value = NULL; + uint32_t *mask = NULL; + struct nlattr *nest; + const char *maskptr; + int ret = 0; + int nbits; + + if (__prefix_0x(arg)) { + force_hex1 = true; + arg += 2; + } + + maskptr = strchr(arg, '/'); + if (maskptr && no_mask) { + parser_err_invalid_value(nlctx, arg); + return -EINVAL; + } + len1 = maskptr ? (unsigned int)(maskptr - arg) : strlen(arg); + nwords = DIV_ROUND_UP(len1, 8); + nbits = 0; + + if (maskptr) { + maskptr++; + if (__prefix_0x(maskptr)) { + maskptr += 2; + force_hex2 = true; + } + len2 = strlen(maskptr); + if (len2 > len1) + nwords = DIV_ROUND_UP(len2, 8); + mask = calloc(nwords, sizeof(uint32_t)); + if (!mask) + return -ENOMEM; + ret = __parse_num_string(maskptr, strlen(maskptr), mask, + force_hex2); + if (ret < 0) { + parser_err_invalid_value(nlctx, arg); + goto out_free; + } + nbits = ret; + } + + value = calloc(nwords, sizeof(uint32_t)); + if (!value) { + free(mask); + return -ENOMEM; + } + ret = __parse_num_string(arg, len1, value, force_hex1); + if (ret < 0) { + parser_err_invalid_value(nlctx, arg); + goto out_free; + } + nbits = (nbits < ret) ? ret : nbits; + nwords = (nbits + 31) / 32; + + ret = 0; + if (!type) + goto out_free; + ret = -EMSGSIZE; + nest = ethnla_nest_start(msgbuff, type); + if (!nest) + goto out_free; + if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, !mask) || + ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_SIZE, nbits) || + ethnla_put(msgbuff, ETHTOOL_A_BITSET_VALUE, + nwords * sizeof(uint32_t), value) || + (mask && + ethnla_put(msgbuff, ETHTOOL_A_BITSET_MASK, + nwords * sizeof(uint32_t), mask))) + goto out_free; + ethnla_nest_end(msgbuff, nest); + ret = 0; + +out_free: + free(value); + free(mask); + nlctx->argp++; + nlctx->argc--; + return ret; +} + +/* Parse bitset provided as series of "name on|off" pairs (@no_mask is false) + * or names (@no_mask is true). Generates bitset nested attribute in verbose + * form with names from command line. + */ +static int parse_name_bitset(struct nl_context *nlctx, uint16_t type, + bool no_mask, struct nl_msg_buff *msgbuff) +{ + struct nlattr *bitset_attr; + struct nlattr *bits_attr; + struct nlattr *bit_attr; + int ret; + + bitset_attr = ethnla_nest_start(msgbuff, type); + if (!bitset_attr) + return -EMSGSIZE; + ret = -EMSGSIZE; + if (no_mask && ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true)) + goto err; + bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS); + if (!bits_attr) + goto err; + + while (nlctx->argc > 0) { + bool bit_val = true; + + if (!strcmp(*nlctx->argp, "--")) { + nlctx->argp++; + nlctx->argc--; + break; + } + ret = -EINVAL; + if (!no_mask) { + if (nlctx->argc < 2 || + (strcmp(nlctx->argp[1], "on") && + strcmp(nlctx->argp[1], "off"))) { + parser_err_invalid_flag(nlctx, *nlctx->argp); + goto err; + } + bit_val = !strcmp(nlctx->argp[1], "on"); + } + + ret = -EMSGSIZE; + bit_attr = ethnla_nest_start(msgbuff, + ETHTOOL_A_BITSET_BITS_BIT); + if (!bit_attr) + goto err; + if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, + nlctx->argp[0])) + goto err; + if (!no_mask && + ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, + bit_val)) + goto err; + ethnla_nest_end(msgbuff, bit_attr); + + nlctx->argp += (no_mask ? 1 : 2); + nlctx->argc -= (no_mask ? 1 : 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 bool is_char_bitset(const char *arg, + const struct char_bitset_parser_data *data) +{ + bool mask = (arg[0] == '+' || arg[0] == '-'); + unsigned int i; + const char *p; + + for (p = arg; *p; p++) { + if (*p == data->reset_char) + continue; + if (mask && (*p == '+' || *p == '-')) + continue; + for (i = 0; i < data->nbits; i++) + if (*p == data->bit_chars[i]) + goto found; + return false; +found: + ; + } + + return true; +} + +/* Parse bitset provided as a string consisting of characters corresponding to + * bit indices. The "reset character" resets the no-mask bitset to empty. If + * the first character is '+' or '-', generated bitset has mask and '+' and + * '-' switch between enabling and disabling the following bits (i.e. their + * value being true/false). In such case, "reset character" is not allowed. + */ +static int parse_char_bitset(struct nl_context *nlctx, uint16_t type, + const struct char_bitset_parser_data *data, + struct nl_msg_buff *msgbuff) +{ + const char *arg = *nlctx->argp; + struct nlattr *bitset_attr; + struct nlattr *saved_pos; + struct nlattr *bits_attr; + struct nlattr *bit_attr; + unsigned int idx; + bool val = true; + const char *p; + bool no_mask; + int ret; + + no_mask = data->no_mask || !(arg[0] == '+' || arg[0] == '-'); + bitset_attr = ethnla_nest_start(msgbuff, type); + if (!bitset_attr) + return -EMSGSIZE; + ret = -EMSGSIZE; + if (no_mask && ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true)) + goto err; + bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS); + if (!bits_attr) + goto err; + saved_pos = mnl_nlmsg_get_payload_tail(msgbuff->nlhdr); + + for (p = arg; *p; p++) { + if (*p == '+' || *p == '-') { + if (no_mask) { + parser_err_invalid_value(nlctx, arg); + ret = -EINVAL; + goto err; + } + val = (*p == '+'); + continue; + } + if (*p == data->reset_char) { + if (no_mask) { + mnl_attr_nest_cancel(msgbuff->nlhdr, saved_pos); + continue; + } + fprintf(stderr, "ethtool (%s): invalid char '%c' in '%s' for parameter '%s'\n", + nlctx->cmd, *p, arg, nlctx->param); + ret = -EINVAL; + goto err; + } + + for (idx = 0; idx < data->nbits; idx++) { + if (data->bit_chars[idx] == *p) + break; + } + if (idx >= data->nbits) { + fprintf(stderr, "ethtool (%s): invalid char '%c' in '%s' for parameter '%s'\n", + nlctx->cmd, *p, arg, nlctx->param); + ret = -EINVAL; + goto err; + } + bit_attr = ethnla_nest_start(msgbuff, + ETHTOOL_A_BITSET_BITS_BIT); + if (!bit_attr) + goto err; + if (ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_BIT_INDEX, idx)) + goto err; + if (!no_mask && + ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, val)) + goto err; + ethnla_nest_end(msgbuff, bit_attr); + } + + ethnla_nest_end(msgbuff, bits_attr); + ethnla_nest_end(msgbuff, bitset_attr); + nlctx->argp++; + nlctx->argc--; + return 0; +err: + ethnla_nest_cancel(msgbuff, bitset_attr); + return ret; +} + +/* Parser handler for bitset. Expects either a numeric value (base 16 or 10 + * (unless force_hex is set)), optionally followed by '/' and another numeric + * value (mask, unless no_mask is set), or a series of "name on|off" pairs + * (no_mask not set) or names (no_mask set). In the latter case, names are + * passed on as they are and kernel performs their interpretation and + * validation. The @data parameter points to struct bitset_parser_data. + * Generates only a bitset nested attribute. Fails if @type is zero or @dest + * is not null. + */ +int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data, + struct nl_msg_buff *msgbuff, void *dest) +{ + const struct bitset_parser_data *parser_data = data; + + if (!type || dest) { + fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n", + nlctx->cmd, nlctx->param); + return -EFAULT; + } + if (is_numeric_bitset(*nlctx->argp, false)) + return parse_numeric_bitset(nlctx, type, parser_data->no_mask, + parser_data->force_hex, msgbuff); + else + return parse_name_bitset(nlctx, type, parser_data->no_mask, + msgbuff); +} + +/* Parser handler for bitset. Expects either a numeric value (base 10 or 16), + * optionally followed by '/' and another numeric value (mask, unless no_mask + * is set), or a string consisting of characters corresponding to bit indices. + * The @data parameter points to struct char_bitset_parser_data. Generates + * biset nested attribute. Fails if type is zero or if @dest is not null. + */ +int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest) +{ + const struct char_bitset_parser_data *parser_data = data; + + if (!type || dest) { + fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n", + nlctx->cmd, nlctx->param); + return -EFAULT; + } + if (is_char_bitset(*nlctx->argp, data) || + !is_numeric_bitset(*nlctx->argp, false)) + return parse_char_bitset(nlctx, type, parser_data, msgbuff); + else + return parse_numeric_bitset(nlctx, type, parser_data->no_mask, + false, msgbuff); +} + +/* parser implementation */ + +static const struct param_parser *find_parser(const struct param_parser *params, + const char *arg) +{ + const struct param_parser *parser; + + for (parser = params; parser->arg; parser++) + if (!strcmp(arg, parser->arg)) + return parser; + return NULL; +} + +static bool __parser_bit(const uint64_t *map, unsigned int idx) +{ + return map[idx / 64] & (1 << (idx % 64)); +} + +static void __parser_set(uint64_t *map, unsigned int idx) +{ + map[idx / 64] |= (1 << (idx % 64)); +} + +static void __parser_set_group(const struct param_parser *params, + uint64_t *map, unsigned int alt_group) +{ + const struct param_parser *parser; + unsigned int idx = 0; + + for (parser = params; parser->arg; parser++, idx++) + if (parser->alt_group == alt_group) + __parser_set(map, idx); +} + +struct tmp_buff { + struct nl_msg_buff *msgbuff; + unsigned int id; + unsigned int orig_len; + struct tmp_buff *next; +}; + +static struct tmp_buff *tmp_buff_find(struct tmp_buff *head, unsigned int id) +{ + struct tmp_buff *buff; + + for (buff = head; buff; buff = buff->next) + if (buff->id == id) + break; + + return buff; +} + +static struct tmp_buff *tmp_buff_find_or_create(struct tmp_buff **phead, + unsigned int id) +{ + struct tmp_buff **pbuff; + struct tmp_buff *new_buff; + + for (pbuff = phead; *pbuff; pbuff = &(*pbuff)->next) + if ((*pbuff)->id == id) + return *pbuff; + + new_buff = malloc(sizeof(*new_buff)); + if (!new_buff) + return NULL; + new_buff->id = id; + 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; + + return new_buff; +} + +static void tmp_buff_destroy(struct tmp_buff *head) +{ + struct tmp_buff *buff = head; + struct tmp_buff *next; + + while (buff) { + next = buff->next; + if (buff->msgbuff) { + msgbuff_done(buff->msgbuff); + free(buff->msgbuff); + } + free(buff); + buff = next; + } +} + +/* Main entry point of parser implementation. + * @nlctx: netlink context + * @params: array of struct param_parser describing expected arguments + * and their handlers; the array must be terminated by null + * element {} + * @dest: optional destination to copy parsed data to (at + * 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, + 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; + int ret; + + n_params = 0; + for (parser = params; parser->arg; parser++) { + struct nl_msg_buff *msgbuff; + struct nlattr *nest; + + n_params++; + if (group_style == PARSER_GROUP_NONE || !parser->group) + continue; + ret = -ENOMEM; + buff = tmp_buff_find_or_create(&buffs, parser->group); + if (!buff) + goto out_free_buffs; + 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); + if (!nest) + goto out_free_buffs; + break; + case PARSER_GROUP_MSG: + if (ethnla_fill_header(msgbuff, + ETHTOOL_A_LINKINFO_HEADER, + nlctx->devname, 0)) + goto out_free_buffs; + break; + default: + break; + } + + buff->orig_len = msgbuff_len(msgbuff); + } + ret = -ENOMEM; + params_seen = calloc(DIV_ROUND_UP(n_params, 64), sizeof(uint64_t)); + if (!params_seen) + goto out_free_buffs; + + while (nlctx->argc > 0) { + struct nl_msg_buff *msgbuff; + void *param_dest; + + nlctx->param = *nlctx->argp; + ret = -EINVAL; + parser = find_parser(params, nlctx->param); + if (!parser) { + parser_err_unknown_param(nlctx); + goto out_free; + } + + /* check duplicates and minimum number of arguments */ + if (__parser_bit(params_seen, parser - params)) { + parser_err_dup_param(nlctx); + goto out_free; + } + nlctx->argc--; + nlctx->argp++; + if (nlctx->argc < parser->min_argc) { + parser_err_min_argc(nlctx, parser->min_argc); + goto out_free; + } + if (parser->alt_group) + __parser_set_group(params, params_seen, + parser->alt_group); + else + __parser_set(params_seen, parser - params); + + buff = NULL; + if (parser->group) + buff = tmp_buff_find(buffs, parser->group); + msgbuff = buff ? buff->msgbuff : &nlsk->msgbuff; + + 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; + + if (group_style == PARSER_GROUP_NONE || + msgbuff_len(msgbuff) == buff->orig_len) + continue; + switch (group_style) { + case PARSER_GROUP_NEST: + ethnla_nest_end(msgbuff, msgbuff->payload); + ret = msgbuff_append(&nlsk->msgbuff, msgbuff); + if (ret < 0) + goto out_free; + break; + case PARSER_GROUP_MSG: + msgbuffs[n_msgbuffs++] = msgbuff; + buff->msgbuff = NULL; + break; + default: + break; + } + } + + ret = 0; +out_free: + free(params_seen); +out_free_buffs: + tmp_buff_destroy(buffs); + return ret; +} diff --git a/netlink/parser.h b/netlink/parser.h new file mode 100644 index 0000000..8a4e8af --- /dev/null +++ b/netlink/parser.h @@ -0,0 +1,153 @@ +/* + * parser.h - netlink command line parser + * + * Interface for command line parser used by netlink code. + */ + +#ifndef ETHTOOL_NETLINK_PARSER_H__ +#define ETHTOOL_NETLINK_PARSER_H__ + +#include <stddef.h> + +#include "netlink.h" + +/* Some commands need to generate multiple netlink messages or multiple nested + * attributes from one set of command line parameters. Argument @group_style + * of nl_parser() takes one of three values: + * + * PARSER_GROUP_NONE - no grouping, flat series of attributes (default) + * PARSER_GROUP_NEST - one nest for each @group value (@group is nest type) + * PARSER_GROUP_MSG - one message for each @group value (@group is command) + */ +enum parser_group_style { + PARSER_GROUP_NONE = 0, + PARSER_GROUP_NEST, + PARSER_GROUP_MSG, +}; + +typedef int (*param_parser_cb_t)(struct nl_context *, uint16_t, const void *, + struct nl_msg_buff *, void *); + +struct param_parser { + /* command line parameter handled by this entry */ + const char *arg; + /* group id (see enum parser_group_style above) */ + unsigned int group; + /* netlink attribute type */ + uint16_t type; /* netlink attribute type */ + /* function called to parse parameter and its value */ + param_parser_cb_t handler; + /* pointer passed as @data argument of the handler */ + const void *handler_data; + /* minimum number of extra arguments after this parameter */ + unsigned int min_argc; + /* if @dest is passed to nl_parser(), offset to store value */ + unsigned int dest_offset; + /* parameter alternative group - only one parameter from a group + * can be specified, 0 means no group + */ + unsigned int alt_group; +}; + +/* data structures used for handler data */ + +/* used by nl_parse_lookup_u32() */ +struct lookup_entry_u32 { + const char *arg; + uint32_t val; +}; + +/* used by nl_parse_lookup_u8() */ +struct lookup_entry_u8 { + const char *arg; + uint8_t val; +}; + +/* used by nl_parse_byte_str() */ +struct byte_str_parser_data { + unsigned int min_len; + unsigned int max_len; + char delim; +}; + +/* used for value stored by nl_parse_byte_str() */ +struct byte_str_value { + unsigned int len; + u8 *data; +}; + +/* used by nl_parse_error() */ +struct error_parser_data { + const char *err_msg; + int ret_val; + unsigned int extra_args; +}; + +/* used by nl_parse_bitset() */ +struct bitset_parser_data { + bool force_hex; + bool no_mask; +}; + +/* used by nl_parse_char_bitset() */ +struct char_bitset_parser_data { + const char *bit_chars; + unsigned int nbits; + char reset_char; + bool no_mask; +}; + +int parse_u32(const char *arg, uint32_t *result); + +/* parser handlers to use as param_parser::handler */ + +/* NLA_FLAG represented by on | off */ +int nl_parse_flag(struct nl_context *nlctx, uint16_t type, const void *data, + struct nl_msg_buff *msgbuff, void *dest); +/* NLA_NUL_STRING represented by a string argument */ +int nl_parse_string(struct nl_context *nlctx, uint16_t type, const void *data, + struct nl_msg_buff *msgbuff, void *dest); +/* NLA_U32 represented as numeric argument */ +int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest); +/* NLA_U8 represented as numeric argument */ +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); +/* NLA_U32 represented by a string argument (lookup table) */ +int nl_parse_lookup_u32(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest); +/* NLA_U8 represented by a string argument (lookup table) */ +int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest); +/* always fail (for deprecated parameters) */ +int nl_parse_error(struct nl_context *nlctx, uint16_t type, const void *data, + struct nl_msg_buff *msgbuff, void *dest); +/* NLA_BINARY represented by %x:%x:...%x (e.g. a MAC address) */ +int nl_parse_byte_str(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest); +/* bitset represented by %x[/%x] or name on|off ... [--] */ +int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data, + struct nl_msg_buff *msgbuff, void *dest); +/* bitset represented by %u[/%u] or string (characters for bits) */ +int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest); + +/* 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, + 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..da444bd --- /dev/null +++ b/netlink/pause.c @@ -0,0 +1,331 @@ +/* + * 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; +} + +static const struct lookup_entry_u32 stats_src_values[] = { + { .arg = "aggregate", .val = ETHTOOL_MAC_STATS_SRC_AGGREGATE }, + { .arg = "emac", .val = ETHTOOL_MAC_STATS_SRC_EMAC }, + { .arg = "pmac", .val = ETHTOOL_MAC_STATS_SRC_PMAC }, + {} +}; + +static const struct param_parser gpause_params[] = { + { + .arg = "--src", + .type = ETHTOOL_A_PAUSE_STATS_SRC, + .handler = nl_parse_lookup_u32, + .handler_data = stats_src_values, + .min_argc = 1, + }, + {} +}; + +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; + + 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; + + nlctx->cmd = "-a"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + + ret = nl_parser(nlctx, gpause_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + 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/permaddr.c b/netlink/permaddr.c new file mode 100644 index 0000000..006eac6 --- /dev/null +++ b/netlink/permaddr.c @@ -0,0 +1,114 @@ +/* + * permaddr.c - netlink implementation of permanent address request + * + * Implementation of "ethtool -P <dev>" + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <linux/rtnetlink.h> +#include <linux/if_link.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" + +/* PERMADDR_GET */ + +static int permaddr_prep_request(struct nl_socket *nlsk) +{ + unsigned int nlm_flags = NLM_F_REQUEST | NLM_F_ACK; + struct nl_context *nlctx = nlsk->nlctx; + const char *devname = nlctx->ctx->devname; + struct nl_msg_buff *msgbuff = &nlsk->msgbuff; + struct ifinfomsg *ifinfo; + struct nlmsghdr *nlhdr; + int ret; + + if (devname && !strcmp(devname, WILDCARD_DEVNAME)) { + devname = NULL; + nlm_flags |= NLM_F_DUMP; + } + nlctx->is_dump = !devname; + + ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE); + if (ret < 0) + return ret; + memset(msgbuff->buff, '\0', NLMSG_HDRLEN + sizeof(*ifinfo)); + + nlhdr = mnl_nlmsg_put_header(msgbuff->buff); + nlhdr->nlmsg_type = RTM_GETLINK; + nlhdr->nlmsg_flags = nlm_flags; + msgbuff->nlhdr = nlhdr; + ifinfo = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*ifinfo)); + + if (devname) { + uint16_t type = IFLA_IFNAME; + + if (strlen(devname) >= IFNAMSIZ) + type = IFLA_ALT_IFNAME; + if (ethnla_put_strz(msgbuff, type, devname)) + return -EMSGSIZE; + } + if (ethnla_put_u32(msgbuff, IFLA_EXT_MASK, RTEXT_FILTER_SKIP_STATS)) + return -EMSGSIZE; + + return 0; +} + +int permaddr_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[__IFLA_MAX] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + const uint8_t *permaddr; + unsigned int i; + int ret; + + if (nlhdr->nlmsg_type != RTM_NEWLINK) + goto err; + ret = mnl_attr_parse(nlhdr, sizeof(struct ifinfomsg), attr_cb, + &tb_info); + if (ret < 0 || !tb[IFLA_IFNAME]) + goto err; + nlctx->devname = mnl_attr_get_str(tb[IFLA_IFNAME]); + if (!dev_ok(nlctx)) + goto err; + + if (!tb[IFLA_PERM_ADDRESS]) { + if (!nlctx->is_dump) + printf("Permanent address: not set\n"); + return MNL_CB_OK; + } + + if (nlctx->is_dump) + printf("Permanent address of %s:", nlctx->devname); + else + printf("Permanent address:"); + permaddr = mnl_attr_get_payload(tb[IFLA_PERM_ADDRESS]); + for (i = 0; i < mnl_attr_get_payload_len(tb[IFLA_PERM_ADDRESS]); i++) + printf("%c%02x", i ? ':' : ' ', permaddr[i]); + putchar('\n'); + return MNL_CB_OK; + +err: + if (nlctx->is_dump || nlctx->is_monitor) + return MNL_CB_OK; + nlctx->exit_code = 2; + return MNL_CB_ERROR; +} + +int nl_permaddr(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + int ret; + + ret = netlink_init_rtnl_socket(nlctx); + if (ret < 0) + return ret; + ret = permaddr_prep_request(nlctx->rtnl_socket); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlctx->rtnl_socket, permaddr_reply_cb); +} diff --git a/netlink/plca.c b/netlink/plca.c new file mode 100644 index 0000000..7d61e3b --- /dev/null +++ b/netlink/plca.c @@ -0,0 +1,296 @@ +/* + * plca.c - netlink implementation of plca command + * + * Implementation of "ethtool --show-plca <dev>" and + * "ethtool --set-plca <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" + +/* PLCA_GET_CFG */ + +int plca_get_cfg_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool silent; + int idv = 255; + int err_ret; + int val; + 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_PLCA_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + putchar('\n'); + + printf("PLCA settings for %s:\n", nlctx->devname); + + // check if PLCA is enabled + printf("\tEnabled: "); + + if (!tb[ETHTOOL_A_PLCA_ENABLED]) { + printf("not supported"); + } else { + val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_ENABLED]); + printf(val ? "Yes" : "No"); + } + putchar('\n'); + + // get node ID + printf("\tlocal node ID: "); + + if (!tb[ETHTOOL_A_PLCA_NODE_ID]) { + printf("not supported"); + } else { + idv = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_NODE_ID]); + printf("%u (%s)", idv, + idv == 0 ? "coordinator" : + idv == 255 ? "unconfigured" : "follower"); + } + putchar('\n'); + + // get node count + printf("\tNode count: "); + if (!tb[ETHTOOL_A_PLCA_NODE_CNT]) { + printf("not supported"); + } else { + val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_NODE_CNT]); + printf("%u", val); + + // The node count is ignored by follower nodes. However, it can + // be pre-set to enable fast coordinator role switchover. + // Therefore, on a follower node we still wanto to show it, + // indicating it is not currently used. + if (tb[ETHTOOL_A_PLCA_NODE_ID] && idv != 0) + printf(" (ignored)"); + } + putchar('\n'); + + // get TO timer (transmit opportunity timer) + printf("\tTO timer: "); + if (!tb[ETHTOOL_A_PLCA_TO_TMR]) { + printf("not supported"); + } else { + val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_TO_TMR]); + printf("%u BT", val); + } + putchar('\n'); + + // get burst count + printf("\tBurst count: "); + if (!tb[ETHTOOL_A_PLCA_BURST_CNT]) { + printf("not supported"); + } else { + val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_BURST_CNT]); + printf("%u (%s)", val, + val > 0 ? "enabled" : "disabled"); + } + putchar('\n'); + + // get burst timer + printf("\tBurst timer: "); + if (!tb[ETHTOOL_A_PLCA_BURST_TMR]) { + printf("not supported"); + } else { + val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_BURST_TMR]); + printf("%u BT", val); + } + putchar('\n'); + + return MNL_CB_OK; +} + + +int nl_plca_get_cfg(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_PLCA_GET_CFG, 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_PLCA_GET_CFG, + ETHTOOL_A_PLCA_HEADER, 0); + + if (ret < 0) + return ret; + + return nlsock_send_get_request(nlsk, plca_get_cfg_reply_cb); +} + +/* PLCA_SET_CFG */ + +static const struct param_parser set_plca_params[] = { + { + .arg = "enable", + .type = ETHTOOL_A_PLCA_ENABLED, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "node-id", + .type = ETHTOOL_A_PLCA_NODE_ID, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "node-cnt", + .type = ETHTOOL_A_PLCA_NODE_CNT, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "to-tmr", + .type = ETHTOOL_A_PLCA_TO_TMR, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "burst-cnt", + .type = ETHTOOL_A_PLCA_BURST_CNT, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "burst-tmr", + .type = ETHTOOL_A_PLCA_BURST_TMR, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + {} +}; + +int nl_plca_set_cfg(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_PLCA_SET_CFG, false)) + return -EOPNOTSUPP; + if (!ctx->argc) { + fprintf(stderr, + "ethtool (--set-plca-cfg): parameters missing\n"); + return 1; + } + + nlctx->cmd = "--set-plca-cfg"; + 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_PLCA_SET_CFG, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_PLCA_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, set_plca_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; +} + +/* PLCA_GET_STATUS */ + +int plca_get_status_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + u8 val; + + 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_PLCA_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + putchar('\n'); + + printf("PLCA status of %s:\n", nlctx->devname); + + // check whether the Open Alliance TC14 standard memory map is supported + printf("\tStatus: "); + + if (!tb[ETHTOOL_A_PLCA_STATUS]) { + printf("not supported"); + } else { + val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_STATUS]); + printf(val ? "on" : "off"); + } + putchar('\n'); + + return MNL_CB_OK; +} + + +int nl_plca_get_status(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_PLCA_GET_STATUS, 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_PLCA_GET_STATUS, + ETHTOOL_A_PLCA_HEADER, 0); + + if (ret < 0) + return ret; + + return nlsock_send_get_request(nlsk, plca_get_status_reply_cb); +} diff --git a/netlink/prettymsg.c b/netlink/prettymsg.c new file mode 100644 index 0000000..fbf684f --- /dev/null +++ b/netlink/prettymsg.c @@ -0,0 +1,262 @@ +/* + * prettymsg.c - human readable message dump + * + * Support for pretty print of an ethtool netlink message + */ + +#include <stdio.h> +#include <string.h> +#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> +#include <libmnl/libmnl.h> + +#include "prettymsg.h" + +#define __INDENT 4 +#define __DUMP_LINE 16 +#define __DUMP_BLOCK 4 + +static void __print_binary_short(uint8_t *adata, unsigned int alen) +{ + unsigned int i; + + if (!alen) + return; + printf("%02x", adata[0]); + for (i = 1; i < alen; i++) + printf("%c%02x", (i % __DUMP_BLOCK) ? ':' : ' ', adata[i]); +} + +static void __print_binary_long(uint8_t *adata, unsigned int alen, + unsigned int level) +{ + unsigned int i; + + for (i = 0; i < alen; i++) { + if (i % __DUMP_LINE == 0) + printf("\n%*s", __INDENT * (level + 2), ""); + else if (i % __DUMP_BLOCK == 0) + printf(" "); + else + putchar(' '); + printf("%02x", adata[i]); + } +} + +static int pretty_print_attr(const struct nlattr *attr, + const struct pretty_nla_desc *desc, + unsigned int ndesc, unsigned int level, + int err_offset, bool in_array) +{ + unsigned int alen = mnl_attr_get_payload_len(attr); + unsigned int atype = mnl_attr_get_type(attr); + unsigned int desc_idx = in_array ? 0 : atype; + void *adata = mnl_attr_get_payload(attr); + const struct pretty_nla_desc *adesc; + const char *prefix = " "; + bool nested; + + adesc = (desc && desc_idx < ndesc) ? &desc[desc_idx] : NULL; + nested = (adesc && (adesc->format == NLA_NESTED || + adesc->format == NLA_ARRAY)) || + (attr->nla_type & NLA_F_NESTED); + if (err_offset >= 0 && + err_offset < (nested ? NLA_HDRLEN : attr->nla_len)) { + prefix = "===>"; + if (err_offset) + fprintf(stderr, + "ethtool: bad_attr inside an attribute (offset %d)\n", + err_offset); + } + if (adesc && adesc->name && !in_array) + printf("%s%*s%s", prefix, level * __INDENT, "", adesc->name); + else + printf("%s%*s[%u]", prefix, level * __INDENT, "", atype); + + if (nested) { + struct nlattr *child; + int ret = 0; + + putchar('\n'); + mnl_attr_for_each_nested(child, attr) { + bool array = adesc && adesc->format == NLA_ARRAY; + unsigned int child_off; + + child_off = (const char *)child - (const char *)attr; + ret = pretty_print_attr(child, + adesc ? adesc->children : NULL, + adesc ? adesc->n_children : 0, + level + 1, + err_offset - child_off, array); + if (ret < 0) + break; + } + + return ret; + } + + printf(" = "); + switch(adesc ? adesc->format : NLA_BINARY) { + case NLA_U8: + printf("%u", mnl_attr_get_u8(attr)); + break; + case NLA_U16: + printf("%u", mnl_attr_get_u16(attr)); + break; + 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; + case NLA_X16: + printf("0x%04x", mnl_attr_get_u16(attr)); + break; + 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; + case NLA_S16: + printf("%d", (int)mnl_attr_get_u16(attr)); + break; + 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; + case NLA_FLAG: + printf("true"); + break; + case NLA_BOOL: + printf("%s", mnl_attr_get_u8(attr) ? "on" : "off"); + break; + case NLA_U8_ENUM: + case NLA_U32_ENUM: { + uint32_t val; + + if (adesc->format == NLA_U8_ENUM) + val = mnl_attr_get_u8(attr); + else + 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); + else + __print_binary_long(adata, alen, level); + } + putchar('\n'); + + return 0; +} + +static int pretty_print_nlmsg(const struct nlmsghdr *nlhdr, + unsigned int payload_offset, + const struct pretty_nla_desc *desc, + unsigned int ndesc, unsigned int err_offset) +{ + const struct nlattr *attr; + int attr_offset; + int ret; + + mnl_attr_for_each(attr, nlhdr, payload_offset) { + attr_offset = (const char *)attr - (const char *)nlhdr; + ret = pretty_print_attr(attr, desc, ndesc, 1, + err_offset - attr_offset, false); + if (ret < 0) + return ret; + } + + return 0; +} + +int pretty_print_genlmsg(const struct nlmsghdr *nlhdr, + const struct pretty_nlmsg_desc *desc, + unsigned int ndesc, unsigned int err_offset) +{ + const struct pretty_nlmsg_desc *msg_desc; + const struct genlmsghdr *genlhdr; + + if (mnl_nlmsg_get_payload_len(nlhdr) < GENL_HDRLEN) { + fprintf(stderr, "ethtool: message too short (%u bytes)\n", + nlhdr->nlmsg_len); + return -EINVAL; + } + genlhdr = mnl_nlmsg_get_payload(nlhdr); + msg_desc = (desc && genlhdr->cmd < ndesc) ? &desc[genlhdr->cmd] : NULL; + if (msg_desc && msg_desc->name) + printf(" %s\n", msg_desc->name); + else + printf(" [%u]\n", genlhdr->cmd); + + return pretty_print_nlmsg(nlhdr, GENL_HDRLEN, + msg_desc ? msg_desc->attrs : NULL, + msg_desc ? msg_desc->n_attrs : 0, err_offset); +} + +static void rtm_link_summary(const struct ifinfomsg *ifinfo) +{ + if (ifinfo->ifi_family) + printf(" family=%u", ifinfo->ifi_family); + if (ifinfo->ifi_type) + printf(" type=0x%04x", ifinfo->ifi_type); + if (ifinfo->ifi_index) + printf(" ifindex=%d", ifinfo->ifi_index); + if (ifinfo->ifi_flags) + printf(" flags=0x%x", ifinfo->ifi_flags); + if (ifinfo->ifi_change) + printf(" change=0x%x", ifinfo->ifi_change); +} + +int pretty_print_rtnlmsg(const struct nlmsghdr *nlhdr, unsigned int err_offset) +{ + const unsigned int idx = (nlhdr->nlmsg_type - RTM_BASE) / 4; + const struct pretty_nlmsg_desc *msg_desc = NULL; + unsigned int hdrlen = USHRT_MAX; + + if (nlhdr->nlmsg_type < rtnl_msg_n_desc) + msg_desc = &rtnl_msg_desc[nlhdr->nlmsg_type]; + if (idx < rtnl_msghdr_n_len) + hdrlen = rtnl_msghdr_lengths[idx]; + if (hdrlen < USHRT_MAX && mnl_nlmsg_get_payload_len(nlhdr) < hdrlen) { + fprintf(stderr, "ethtool: message too short (%u bytes)\n", + nlhdr->nlmsg_len); + return -EINVAL; + } + if (msg_desc && msg_desc->name) + printf(" %s", msg_desc->name); + else + printf(" [%u]", nlhdr->nlmsg_type); + if (idx == (RTM_NEWLINK - RTM_BASE) / 4) + rtm_link_summary(mnl_nlmsg_get_payload(nlhdr)); + putchar('\n'); + if (hdrlen == USHRT_MAX) + return 0; + + return pretty_print_nlmsg(nlhdr, hdrlen, + msg_desc ? msg_desc->attrs : NULL, + msg_desc ? msg_desc->n_attrs : 0, err_offset); +} diff --git a/netlink/prettymsg.h b/netlink/prettymsg.h new file mode 100644 index 0000000..8ca1db3 --- /dev/null +++ b/netlink/prettymsg.h @@ -0,0 +1,146 @@ +/* + * prettymsg.h - human readable message dump + * + * Support for pretty print of an ethtool netlink message + */ + +#ifndef ETHTOOL_NETLINK_PRETTYMSG_H__ +#define ETHTOOL_NETLINK_PRETTYMSG_H__ + +#include <linux/netlink.h> + +/* data structures for message format descriptions */ + +enum pretty_nla_format { + NLA_INVALID, + NLA_BINARY, + 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_U8_ENUM, + NLA_U32_ENUM, +}; + +struct pretty_nla_desc { + enum pretty_nla_format format; + const char *name; + union { + const struct pretty_nla_desc *children; + const char *const *names; + }; + union { + unsigned int n_children; + unsigned int n_names; + }; +}; + +struct pretty_nlmsg_desc { + const char *name; + const struct pretty_nla_desc *attrs; + unsigned int n_attrs; +}; + +/* helper macros for message format descriptions */ + +#define NLATTR_DESC(_name, _fmt) \ + [_name] = { \ + .format = _fmt, \ + .name = #_name, \ + } + +#define NLATTR_DESC_INVALID(_name) NLATTR_DESC(_name, NLA_INVALID) +#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) +#define NLATTR_DESC_BINARY(_name) NLATTR_DESC(_name, NLA_BINARY) + +#define NLATTR_DESC_NESTED(_name, _children_desc) \ + [_name] = { \ + .format = NLA_NESTED, \ + .name = #_name, \ + .children = __ ## _children_desc ## _desc, \ + .n_children = ARRAY_SIZE(__ ## _children_desc ## _desc), \ + } +#define NLATTR_DESC_NESTED_NODESC(_name) NLATTR_DESC(_name, NLA_NESTED) +#define NLATTR_DESC_ARRAY(_name, _children_desc) \ + [_name] = { \ + .format = NLA_ARRAY, \ + .name = #_name, \ + .children = __ ## _children_desc ## _desc, \ + .n_children = 1, \ + } +#define NLATTR_DESC_U8_ENUM(_name, _names_table) \ + [_name] = { \ + .format = NLA_U8_ENUM, \ + .name = #_name, \ + .names = __ ## _names_table ## _names, \ + .n_children = ARRAY_SIZE(__ ## _names_table ## _names), \ + } +#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] = { \ + .name = #_name, \ + .attrs = __ ## _attrs ## _desc, \ + .n_attrs = ARRAY_SIZE(__ ## _attrs ## _desc), \ + } + +#define NLMSG_DESC_INVALID(_name) \ + [_name] = { \ + .name = #_name, \ + } + +/* function to pretty print a genetlink message */ +int pretty_print_genlmsg(const struct nlmsghdr *nlhdr, + const struct pretty_nlmsg_desc *desc, + unsigned int ndesc, unsigned int err_offset); +int pretty_print_rtnlmsg(const struct nlmsghdr *nlhdr, unsigned int err_offset); + +/* message descriptions */ + +extern const struct pretty_nlmsg_desc ethnl_umsg_desc[]; +extern const unsigned int ethnl_umsg_n_desc; +extern const struct pretty_nlmsg_desc ethnl_kmsg_desc[]; +extern const unsigned int ethnl_kmsg_n_desc; + +extern const struct pretty_nlmsg_desc genlctrl_msg_desc[]; +extern const unsigned int genlctrl_msg_n_desc; + +extern const struct pretty_nlmsg_desc rtnl_msg_desc[]; +extern const unsigned int rtnl_msg_n_desc; +extern const unsigned short rtnl_msghdr_lengths[]; +extern const unsigned int rtnl_msghdr_n_len; + +#endif /* ETHTOOL_NETLINK_PRETTYMSG_H__ */ 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/pse-pd.c b/netlink/pse-pd.c new file mode 100644 index 0000000..d6faff8 --- /dev/null +++ b/netlink/pse-pd.c @@ -0,0 +1,193 @@ +/* + * pse.c - netlink implementation of pse commands + * + * Implementation of "ethtool --show-pse <dev>" and + * "ethtool --set-pse <dev> ..." + */ + +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" + +/* PSE_GET */ + +static const char *podl_pse_admin_state_name(u32 val) +{ + switch (val) { + case ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN: + return "unknown"; + case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: + return "disabled"; + case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: + return "enabled"; + default: + return "unsupported"; + } +} + +static const char *podl_pse_pw_d_status_name(u32 val) +{ + switch (val) { + case ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN: + return "unknown"; + case ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED: + return "disabled"; + case ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING: + return "searching"; + case ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING: + return "delivering power"; + case ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP: + return "sleep"; + case ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE: + return "idle"; + case ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR: + return "error"; + default: + return "unsupported"; + } +} + +int pse_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_PSE_MAX + 1] = {}; + struct nl_context *nlctx = data; + DECLARE_ATTR_TB_INFO(tb); + 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_PSE_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + print_nl(); + + open_json_object(NULL); + + print_string(PRINT_ANY, "ifname", "PSE attributes for %s:\n", + nlctx->devname); + + if (tb[ETHTOOL_A_PODL_PSE_ADMIN_STATE]) { + u32 val; + + val = mnl_attr_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_STATE]); + print_string(PRINT_ANY, "podl-pse-admin-state", + "PoDL PSE Admin State: %s\n", + podl_pse_admin_state_name(val)); + } + + if (tb[ETHTOOL_A_PODL_PSE_PW_D_STATUS]) { + u32 val; + + val = mnl_attr_get_u32(tb[ETHTOOL_A_PODL_PSE_PW_D_STATUS]); + print_string(PRINT_ANY, "podl-pse-power-detection-status", + "PoDL PSE Power Detection Status: %s\n", + podl_pse_pw_d_status_name(val)); + } + + close_json_object(); + + return MNL_CB_OK; +} + +int nl_gpse(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_PSE_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + nlsk = nlctx->ethnl_socket; + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PSE_GET, + ETHTOOL_A_PSE_HEADER, 0); + if (ret < 0) + return ret; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, pse_reply_cb); + delete_json_obj(); + + return ret; +} + +/* PSE_SET */ + +static const struct lookup_entry_u32 podl_pse_admin_control_values[] = { + { .arg = "enable", .val = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED }, + { .arg = "disable", .val = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED }, + {} +}; + +static const struct param_parser spse_params[] = { + { + .arg = "podl-pse-admin-control", + .type = ETHTOOL_A_PODL_PSE_ADMIN_CONTROL, + .handler = nl_parse_lookup_u32, + .handler_data = podl_pse_admin_control_values, + .min_argc = 1, + }, + {} +}; + +int nl_spse(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_PSE_SET, false)) + return -EOPNOTSUPP; + if (!ctx->argc) { + fprintf(stderr, "ethtool (--set-pse): parameters missing\n"); + return 1; + } + + nlctx->cmd = "--set-pse"; + 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_PSE_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_PSE_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, spse_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 83; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 83; +} diff --git a/netlink/rings.c b/netlink/rings.c new file mode 100644 index 0000000..51d28c2 --- /dev/null +++ b/netlink/rings.c @@ -0,0 +1,214 @@ +/* + * 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; + unsigned char tcp_hds; + char *tcp_hds_fmt; + char *tcp_hds_key; + char tcp_hds_buf[256]; + 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; + + open_json_object(NULL); + + if (silent) + show_cr(); + print_string(PRINT_ANY, "ifname", "Ring parameters for %s:\n", + nlctx->devname); + print_string(PRINT_FP, NULL, "Pre-set maximums:\n", NULL); + show_u32("rx-max", "RX:\t\t\t", tb[ETHTOOL_A_RINGS_RX_MAX]); + show_u32("rx-mini-max", "RX Mini:\t\t", tb[ETHTOOL_A_RINGS_RX_MINI_MAX]); + show_u32("rx-jumbo-max", "RX Jumbo:\t\t", + tb[ETHTOOL_A_RINGS_RX_JUMBO_MAX]); + show_u32("tx-max", "TX:\t\t\t", tb[ETHTOOL_A_RINGS_TX_MAX]); + show_u32("tx-push-buff-max-len", "TX push buff len:\t", + tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX]); + print_string(PRINT_FP, NULL, "Current hardware settings:\n", NULL); + show_u32("rx", "RX:\t\t\t", tb[ETHTOOL_A_RINGS_RX]); + show_u32("rx-mini", "RX Mini:\t\t", tb[ETHTOOL_A_RINGS_RX_MINI]); + show_u32("rx-jumbo", "RX Jumbo:\t\t", tb[ETHTOOL_A_RINGS_RX_JUMBO]); + show_u32("tx", "TX:\t\t\t", tb[ETHTOOL_A_RINGS_TX]); + show_u32("rx-buf-len", "RX Buf Len:\t\t", tb[ETHTOOL_A_RINGS_RX_BUF_LEN]); + show_u32("cqe-size", "CQE Size:\t\t", tb[ETHTOOL_A_RINGS_CQE_SIZE]); + show_bool("tx-push", "TX Push:\t\t%s\n", tb[ETHTOOL_A_RINGS_TX_PUSH]); + show_bool("rx-push", "RX Push:\t\t%s\n", tb[ETHTOOL_A_RINGS_RX_PUSH]); + show_u32("tx-push-buf-len", "TX push buff len:\t", + tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN]); + + tcp_hds_fmt = "TCP data split:\t\t%s\n"; + tcp_hds_key = "tcp-data-split"; + tcp_hds = tb[ETHTOOL_A_RINGS_TCP_DATA_SPLIT] ? + mnl_attr_get_u8(tb[ETHTOOL_A_RINGS_TCP_DATA_SPLIT]) : 0; + switch (tcp_hds) { + case ETHTOOL_TCP_DATA_SPLIT_UNKNOWN: + print_string(PRINT_FP, tcp_hds_key, tcp_hds_fmt, "n/a"); + break; + case ETHTOOL_TCP_DATA_SPLIT_DISABLED: + print_string(PRINT_ANY, tcp_hds_key, tcp_hds_fmt, "off"); + break; + case ETHTOOL_TCP_DATA_SPLIT_ENABLED: + print_string(PRINT_ANY, tcp_hds_key, tcp_hds_fmt, "on"); + break; + default: + snprintf(tcp_hds_buf, sizeof(tcp_hds_buf), + "unknown(%d)\n", tcp_hds); + print_string(PRINT_ANY, tcp_hds_key, tcp_hds_fmt, tcp_hds_buf); + break; + } + + close_json_object(); + + 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; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, rings_reply_cb); + delete_json_obj(); + return ret; +} + +/* 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, + }, + { + .arg = "tx-push-buf-len", + .type = ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-buf-len", + .type = ETHTOOL_A_RINGS_RX_BUF_LEN, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "cqe-size", + .type = ETHTOOL_A_RINGS_CQE_SIZE, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-push", + .type = ETHTOOL_A_RINGS_TX_PUSH, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "rx-push", + .type = ETHTOOL_A_RINGS_RX_PUSH, + .handler = nl_parse_u8bool, + .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/rss.c b/netlink/rss.c new file mode 100644 index 0000000..4ad6065 --- /dev/null +++ b/netlink/rss.c @@ -0,0 +1,230 @@ +/* + * rss.c - netlink implementation of RSS context commands + * + * Implementation of "ethtool -x <dev>" + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "strset.h" +#include "parser.h" + +struct cb_args { + struct nl_context *nlctx; + u32 num_rings; +}; + +void dump_json_rss_info(struct cmd_context *ctx, u32 *indir_table, + u32 indir_size, u8 *hkey, u32 hkey_size, + const struct stringset *hash_funcs, u8 hfunc) +{ + unsigned int i; + + open_json_object(NULL); + print_string(PRINT_JSON, "ifname", NULL, ctx->devname); + if (indir_size) { + open_json_array("rss-indirection-table", NULL); + for (i = 0; i < indir_size; i++) + print_uint(PRINT_JSON, NULL, NULL, indir_table[i]); + close_json_array("\n"); + } + + if (hkey_size) { + open_json_array("rss-hash-key", NULL); + for (i = 0; i < hkey_size; i++) + print_uint(PRINT_JSON, NULL, NULL, (u8)hkey[i]); + close_json_array("\n"); + } + + if (hfunc) { + for (i = 0; i < get_count(hash_funcs); i++) { + if (hfunc & (1 << i)) { + print_string(PRINT_JSON, "rss-hash-function", + NULL, get_string(hash_funcs, i)); + break; + } + } + } + + close_json_object(); +} + +int get_channels_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_CHANNELS_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct cb_args *args = data; + struct nl_context *nlctx = args->nlctx; + 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 (tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT]) + args->num_rings = mnl_attr_get_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT]); + if (tb[ETHTOOL_A_CHANNELS_RX_COUNT]) + args->num_rings += mnl_attr_get_u32(tb[ETHTOOL_A_CHANNELS_RX_COUNT]); + return MNL_CB_OK; +} + +int rss_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_RSS_MAX + 1] = {}; + unsigned int indir_bytes = 0, hkey_bytes = 0; + DECLARE_ATTR_TB_INFO(tb); + struct cb_args *args = data; + struct nl_context *nlctx = args->nlctx; + const struct stringset *hash_funcs; + u32 rss_hfunc = 0, indir_size; + u32 *indir_table = NULL; + u8 *hkey = NULL; + 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_RSS_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + show_cr(); + + if (tb[ETHTOOL_A_RSS_HFUNC]) + rss_hfunc = mnl_attr_get_u32(tb[ETHTOOL_A_RSS_HFUNC]); + + if (tb[ETHTOOL_A_RSS_INDIR]) { + indir_bytes = mnl_attr_get_payload_len(tb[ETHTOOL_A_RSS_INDIR]); + indir_table = mnl_attr_get_payload(tb[ETHTOOL_A_RSS_INDIR]); + } + + if (tb[ETHTOOL_A_RSS_HKEY]) { + hkey_bytes = mnl_attr_get_payload_len(tb[ETHTOOL_A_RSS_HKEY]); + hkey = mnl_attr_get_payload(tb[ETHTOOL_A_RSS_HKEY]); + } + + /* Fetch RSS hash functions and their status and print */ + if (!nlctx->is_monitor) { + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return MNL_CB_ERROR; + } + hash_funcs = global_stringset(ETH_SS_RSS_HASH_FUNCS, + 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_RSS_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + /* Fetch ring count info into args->num_rings */ + ret = nlsock_prep_get_request(nlctx->ethnl2_socket, + ETHTOOL_MSG_CHANNELS_GET, + ETHTOOL_A_CHANNELS_HEADER, 0); + if (ret < 0) + return MNL_CB_ERROR; + + ret = nlsock_sendmsg(nlctx->ethnl2_socket, NULL); + if (ret < 0) + return MNL_CB_ERROR; + + ret = nlsock_process_reply(nlctx->ethnl2_socket, get_channels_cb, args); + if (ret < 0) + return MNL_CB_ERROR; + + indir_size = indir_bytes / sizeof(u32); + if (is_json_context()) { + dump_json_rss_info(nlctx->ctx, (u32 *)indir_table, indir_size, + hkey, hkey_bytes, hash_funcs, rss_hfunc); + } else { + print_indir_table(nlctx->ctx, args->num_rings, + indir_size, (u32 *)indir_table); + print_rss_hkey(hkey, hkey_bytes); + printf("RSS hash function:\n"); + if (!rss_hfunc) { + printf(" Operation not supported\n"); + return 0; + } + for (unsigned int i = 0; i < get_count(hash_funcs); i++) { + printf(" %s: %s\n", get_string(hash_funcs, i), + (rss_hfunc & (1 << i)) ? "on" : "off"); + } + } + + return MNL_CB_OK; +} + +/* RSS_GET */ +static const struct param_parser grss_params[] = { + { + .arg = "context", + .type = ETHTOOL_A_RSS_CONTEXT, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + {} +}; + +int nl_grss(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + struct nl_msg_buff *msgbuff; + struct cb_args args = {}; + int ret; + + nlctx->cmd = "-x"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_RSS_GET, true)) + return -EOPNOTSUPP; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_RSS_GET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 1; + + if (ethnla_fill_header(msgbuff, ETHTOOL_A_RSS_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, grss_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + goto err; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + goto err; + + args.nlctx = nlctx; + new_json_obj(ctx->json); + ret = nlsock_process_reply(nlsk, rss_reply_cb, &args); + delete_json_obj(); + + if (ret == 0) + return 0; +err: + return nlctx->exit_code ?: 1; +} diff --git a/netlink/settings.c b/netlink/settings.c new file mode 100644 index 0000000..a506618 --- /dev/null +++ b/netlink/settings.c @@ -0,0 +1,1377 @@ +/* + * settings.c - netlink implementation of settings commands + * + * Implementation of "ethtool <dev>" and "ethtool -s <dev> ...". + */ + +#include <errno.h> +#include <inttypes.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" + +/* GET_SETTINGS */ + +struct link_mode_info { + enum link_mode_class class; + u32 speed; + u8 duplex; +}; + +static const char *const names_duplex[] = { + [DUPLEX_HALF] = "Half", + [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", + [PORT_BNC] = "BNC", + [PORT_MII] = "MII", + [PORT_FIBRE] = "FIBRE", + [PORT_DA] = "Direct Attach Copper", + [PORT_NONE] = "None", + [PORT_OTHER] = "Other", +}; + +static const char *const names_transceiver[] = { + [XCVR_INTERNAL] = "internal", + [XCVR_EXTERNAL] = "external", +}; + +/* the practice of putting completely unrelated flags into link mode bitmaps + * is rather unfortunate but as even ethtool_link_ksettings preserved that, + * 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] = __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), + [ETHTOOL_LINK_MODE_10baseT1L_Full_BIT] = __REAL(10), + [ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT] = __REAL(800000), + [ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT] = __REAL(800000), + [ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT] = __REAL(800000), + [ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT] = __REAL(800000), + [ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT] = __REAL(800000), + [ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT] = __REAL(800000), + [ETHTOOL_LINK_MODE_10baseT1S_Full_BIT] = __REAL(10), + [ETHTOOL_LINK_MODE_10baseT1S_Half_BIT] = __HALF_DUPLEX(10), + [ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT] = __HALF_DUPLEX(10), +}; +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) ? + link_modes[mode].class : LM_CLASS_UNKNOWN; + + return mode_class == class || + (class == LM_CLASS_REAL && mode_class == LM_CLASS_UNKNOWN); +} + +static void print_enum(const char *const *info, unsigned int n_info, + unsigned int val, const char *label) +{ + if (val >= n_info || !info[val]) + printf("\t%s: Unknown! (%d)\n", label, val); + else + printf("\t%s: %s\n", label, info[val]); +} + +static int dump_pause(const struct nlattr *attr, bool mask, const char *label) +{ + bool pause, asym; + int ret = 0; + + pause = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Pause_BIT, &ret); + if (ret < 0) + goto err; + asym = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Asym_Pause_BIT, + &ret); + if (ret < 0) + goto err; + + printf("\t%s", label); + if (pause) + printf("%s\n", asym ? "Symmetric Receive-only" : "Symmetric"); + else + printf("%s\n", asym ? "Transmit-only" : "No"); + + return 0; +err: + fprintf(stderr, "malformed netlink message (pause modes)\n"); + return ret; +} + +static void print_banner(struct nl_context *nlctx) +{ + if (nlctx->no_banner) + return; + printf("Settings for %s:\n", nlctx->devname); + nlctx->no_banner = true; +} + +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; + bool nomask; + int ret; + + ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info); + 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; + unsigned int idx; + const char *name; + + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + goto err_nonl; + lm_strings = global_stringset(ETH_SS_LINK_MODES, + nlctx->ethnl2_socket); + bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] : + bitset_tb[ETHTOOL_A_BITSET_VALUE]; + ret = -EFAULT; + if (!bits || !bitset_tb[ETHTOOL_A_BITSET_SIZE]) + goto err_nonl; + count = mnl_attr_get_u32(bitset_tb[ETHTOOL_A_BITSET_SIZE]); + if (mnl_attr_get_payload_len(bits) / 4 < (count + 31) / 32) + goto err_nonl; + + printf("\t%s", before); + for (idx = 0; idx < count; idx++) { + const uint32_t *raw_data = mnl_attr_get_payload(bits); + char buff[14]; + + if (!(raw_data[idx / 32] & (1U << (idx % 32)))) + continue; + if (!lm_class_match(idx, class)) + continue; + name = get_string(lm_strings, idx); + if (!name) { + snprintf(buff, sizeof(buff), "BIT%u", idx); + name = buff; + } + if (first) + first = false; + /* ugly hack to preserve old output format */ + 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) + putchar(' '); + else if (between) + printf("\t%s", between); + else + printf("\n\t%*s", before_len, ""); + printf("%s", name); + prev = idx; + } + goto after; + } + + printf("\t%s", before); + mnl_attr_for_each_nested(bit, bits) { + const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + unsigned int idx; + const char *name; + + if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT) + continue; + ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info); + if (ret < 0) + goto err; + ret = -EFAULT; + if (!tb[ETHTOOL_A_BITSET_BIT_INDEX] || + !tb[ETHTOOL_A_BITSET_BIT_NAME]) + goto err; + if (!mask && !nomask && !tb[ETHTOOL_A_BITSET_BIT_VALUE]) + continue; + + idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]); + name = mnl_attr_get_str(tb[ETHTOOL_A_BITSET_BIT_NAME]); + if (!lm_class_match(idx, class)) + continue; + if (first) { + first = false; + } else { + /* ugly hack to preserve old output format */ + 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)) + putchar(' '); + else if (between) + printf("\t%s", between); + else + printf("\n\t%*s", before_len, ""); + } + printf("%s", name); + prev = idx; + } +after: + if (first && if_none) + printf("%s", if_none); + printf("%s", after); + + return 0; +err: + putchar('\n'); +err_nonl: + fflush(stdout); + fprintf(stderr, "malformed netlink message (link_modes)\n"); + return ret; +} + +static int dump_our_modes(struct nl_context *nlctx, const struct nlattr *attr) +{ + bool autoneg; + int ret; + + print_banner(nlctx); + ret = dump_link_modes(nlctx, attr, true, LM_CLASS_PORT, + "Supported ports: [ ", " ", " ]\n", NULL); + if (ret < 0) + return ret; + + ret = dump_link_modes(nlctx, attr, true, LM_CLASS_REAL, + "Supported link modes: ", NULL, "\n", + "Not reported"); + if (ret < 0) + return ret; + ret = dump_pause(attr, true, "Supported pause frame use: "); + if (ret < 0) + return ret; + + autoneg = bitset_get_bit(attr, true, ETHTOOL_LINK_MODE_Autoneg_BIT, + &ret); + if (ret < 0) + return ret; + printf("\tSupports auto-negotiation: %s\n", autoneg ? "Yes" : "No"); + + ret = dump_link_modes(nlctx, attr, true, LM_CLASS_FEC, + "Supported FEC modes: ", " ", "\n", + "Not reported"); + if (ret < 0) + return ret; + + ret = dump_link_modes(nlctx, attr, false, LM_CLASS_REAL, + "Advertised link modes: ", NULL, "\n", + "Not reported"); + if (ret < 0) + return ret; + + ret = dump_pause(attr, false, "Advertised pause frame use: "); + if (ret < 0) + return ret; + autoneg = bitset_get_bit(attr, false, ETHTOOL_LINK_MODE_Autoneg_BIT, + &ret); + if (ret < 0) + return ret; + printf("\tAdvertised auto-negotiation: %s\n", autoneg ? "Yes" : "No"); + + ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC, + "Advertised FEC modes: ", " ", "\n", + "Not reported"); + return ret; +} + +static int dump_peer_modes(struct nl_context *nlctx, const struct nlattr *attr) +{ + bool autoneg; + int ret; + + print_banner(nlctx); + ret = dump_link_modes(nlctx, attr, false, LM_CLASS_REAL, + "Link partner advertised link modes: ", + NULL, "\n", "Not reported"); + if (ret < 0) + return ret; + + ret = dump_pause(attr, false, + "Link partner advertised pause frame use: "); + if (ret < 0) + return ret; + + autoneg = bitset_get_bit(attr, false, + ETHTOOL_LINK_MODE_Autoneg_BIT, &ret); + if (ret < 0) + return ret; + printf("\tLink partner advertised auto-negotiation: %s\n", + autoneg ? "Yes" : "No"); + + ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC, + "Link partner advertised FEC modes: ", + " ", "\n", "Not reported"); + return ret; +} + +int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + int ret; + + if (nlctx->is_dump || nlctx->is_monitor) + nlctx->no_banner = false; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKMODES_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (tb[ETHTOOL_A_LINKMODES_OURS]) { + ret = dump_our_modes(nlctx, tb[ETHTOOL_A_LINKMODES_OURS]); + if (ret < 0) + goto err; + } + if (tb[ETHTOOL_A_LINKMODES_PEER]) { + ret = dump_peer_modes(nlctx, tb[ETHTOOL_A_LINKMODES_PEER]); + if (ret < 0) + goto err; + } + if (tb[ETHTOOL_A_LINKMODES_SPEED]) { + uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKMODES_SPEED]); + + print_banner(nlctx); + if (val == 0 || val == (uint16_t)(-1) || val == (uint32_t)(-1)) + printf("\tSpeed: Unknown!\n"); + else + printf("\tSpeed: %uMb/s\n", val); + } + if (tb[ETHTOOL_A_LINKMODES_LANES]) { + uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKMODES_LANES]); + + print_banner(nlctx); + printf("\tLanes: %u\n", val); + } + if (tb[ETHTOOL_A_LINKMODES_DUPLEX]) { + uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_DUPLEX]); + + print_banner(nlctx); + print_enum(names_duplex, ARRAY_SIZE(names_duplex), val, + "Duplex"); + } + if (tb[ETHTOOL_A_LINKMODES_AUTONEG]) { + int autoneg = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]); + + print_banner(nlctx); + 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: + if (nlctx->is_monitor || nlctx->is_dump) + return MNL_CB_OK; + fputs("No data available\n", stdout); + nlctx->exit_code = 75; + return MNL_CB_ERROR; +} + +int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_LINKINFO_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + int port = -1; + int ret; + + if (nlctx->is_dump || nlctx->is_monitor) + nlctx->no_banner = false; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKINFO_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (tb[ETHTOOL_A_LINKINFO_PORT]) { + uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_PORT]); + + print_banner(nlctx); + print_enum(names_port, ARRAY_SIZE(names_port), val, "Port"); + port = val; + } + if (tb[ETHTOOL_A_LINKINFO_PHYADDR]) { + uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_PHYADDR]); + + print_banner(nlctx); + printf("\tPHYAD: %u\n", val); + } + if (tb[ETHTOOL_A_LINKINFO_TRANSCEIVER]) { + uint8_t val; + + val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TRANSCEIVER]); + print_banner(nlctx); + print_enum(names_transceiver, ARRAY_SIZE(names_transceiver), + val, "Transceiver"); + } + if (tb[ETHTOOL_A_LINKINFO_TP_MDIX] && tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] && + port == PORT_TP) { + uint8_t mdix = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TP_MDIX]); + uint8_t mdix_ctrl = + mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL]); + + print_banner(nlctx); + dump_mdix(mdix, mdix_ctrl); + } + + 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", + [ETHTOOL_LINK_EXT_STATE_MODULE] = "Module", +}; + +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", + [ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST] = + "Serdes reference clock lost", + [ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS] = + "Serdes ALOS", +}; + +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 *const names_module_link_ext_substate[] = { + [ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY] = + "CMIS module is not in ModuleReady state", +}; + +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); + case ETHTOOL_LINK_EXT_STATE_MODULE: + return get_enum_string(names_module_link_ext_substate, + ARRAY_SIZE(names_module_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] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + int ret; + + if (nlctx->is_dump || nlctx->is_monitor) + nlctx->no_banner = false; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKSTATE_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (tb[ETHTOOL_A_LINKSTATE_LINK]) { + uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_LINK]); + + print_banner(nlctx); + 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"); + } + } + + if (tb[ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT]) { + uint32_t val; + + val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT]); + printf("\tLink Down Events: %u\n", val); + } + + return MNL_CB_OK; +} + +void wol_modes_cb(unsigned int idx, const char *name __maybe_unused, bool val, + void *data) +{ + struct ethtool_wolinfo *wol = data; + + if (idx >= 32) + return; + wol->supported |= (1U << idx); + if (val) + wol->wolopts |= (1U << idx); +} + +int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_WOL_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + struct ethtool_wolinfo wol = {}; + int ret; + + if (nlctx->is_dump || nlctx->is_monitor) + nlctx->no_banner = false; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_WOL_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (tb[ETHTOOL_A_WOL_MODES]) + walk_bitset(tb[ETHTOOL_A_WOL_MODES], NULL, wol_modes_cb, &wol); + if (tb[ETHTOOL_A_WOL_SOPASS]) { + unsigned int len; + + len = mnl_attr_get_payload_len(tb[ETHTOOL_A_WOL_SOPASS]); + if (len != SOPASS_MAX) + fprintf(stderr, "invalid SecureOn password length %u (should be %u)\n", + len, SOPASS_MAX); + else + memcpy(wol.sopass, + mnl_attr_get_payload(tb[ETHTOOL_A_WOL_SOPASS]), + SOPASS_MAX); + } + print_banner(nlctx); + dump_wol(&wol); + + return MNL_CB_OK; +} + +void msgmask_cb(unsigned int idx, const char *name __maybe_unused, bool val, + void *data) +{ + u32 *msg_mask = data; + + if (idx >= 32) + return; + if (val) + *msg_mask |= (1U << idx); +} + +void msgmask_cb2(unsigned int idx __maybe_unused, const char *name, + bool val, void *data __maybe_unused) +{ + if (val) + printf(" %s", name); +} + +int debug_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_DEBUG_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + const struct stringset *msgmask_strings = NULL; + struct nl_context *nlctx = data; + u32 msg_mask = 0; + int ret; + + if (nlctx->is_dump || nlctx->is_monitor) + nlctx->no_banner = false; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_DEBUG_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (!tb[ETHTOOL_A_DEBUG_MSGMASK]) + return MNL_CB_OK; + if (bitset_is_compact(tb[ETHTOOL_A_DEBUG_MSGMASK])) { + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return MNL_CB_OK; + msgmask_strings = global_stringset(ETH_SS_MSG_CLASSES, + nlctx->ethnl2_socket); + } + + print_banner(nlctx); + walk_bitset(tb[ETHTOOL_A_DEBUG_MSGMASK], NULL, msgmask_cb, &msg_mask); + printf(" Current message level: 0x%08x (%u)\n" + " ", + msg_mask, msg_mask); + walk_bitset(tb[ETHTOOL_A_DEBUG_MSGMASK], msgmask_strings, msgmask_cb2, + NULL); + fputc('\n', stdout); + + return MNL_CB_OK; +} + +int plca_cfg_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + int ret; + + if (nlctx->is_dump || nlctx->is_monitor) + nlctx->no_banner = false; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + print_banner(nlctx); + printf("\tPLCA support: "); + + if (tb[ETHTOOL_A_PLCA_VERSION]) { + uint16_t val = mnl_attr_get_u16(tb[ETHTOOL_A_PLCA_VERSION]); + + printf("OPEN Alliance v%u.%u", + (unsigned int)((val >> 4) & 0xF), + (unsigned int)(val & 0xF)); + } else + printf("non-standard"); + + printf("\n"); + + return MNL_CB_OK; +} + +int plca_status_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + int ret; + + if (nlctx->is_dump || nlctx->is_monitor) + nlctx->no_banner = false; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + print_banner(nlctx); + printf("\tPLCA status: "); + + if (tb[ETHTOOL_A_PLCA_STATUS]) { + uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_STATUS]); + + printf(val ? "up" : "down"); + } else + printf("unknown"); + + printf("\n"); + + return MNL_CB_OK; +} + +static int gset_request(struct cmd_context *ctx, uint8_t msg_type, + uint16_t hdr_attr, mnl_cb_t cb) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + u32 flags; + int ret; + + if (netlink_cmd_check(ctx, msg_type, true)) + return 0; + + flags = get_stats_flag(nlctx, msg_type, hdr_attr); + + ret = nlsock_prep_get_request(nlsk, msg_type, hdr_attr, flags); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, cb); +} + +int nl_gset(struct cmd_context *ctx) +{ + int ret; + + /* Check for the base set of commands */ + 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; + + ctx->nlctx->suppress_nlerr = 1; + + ret = gset_request(ctx, ETHTOOL_MSG_LINKMODES_GET, + ETHTOOL_A_LINKMODES_HEADER, linkmodes_reply_cb); + if (ret == -ENODEV) + return ret; + + ret = gset_request(ctx, ETHTOOL_MSG_LINKINFO_GET, + ETHTOOL_A_LINKINFO_HEADER, linkinfo_reply_cb); + if (ret == -ENODEV) + return ret; + + ret = gset_request(ctx, ETHTOOL_MSG_WOL_GET, ETHTOOL_A_WOL_HEADER, + wol_reply_cb); + if (ret == -ENODEV) + return ret; + + ret = gset_request(ctx, ETHTOOL_MSG_PLCA_GET_CFG, + ETHTOOL_A_PLCA_HEADER, plca_cfg_reply_cb); + if (ret == -ENODEV) + return ret; + + ret = gset_request(ctx, ETHTOOL_MSG_DEBUG_GET, ETHTOOL_A_DEBUG_HEADER, + debug_reply_cb); + if (ret == -ENODEV) + return ret; + + ret = gset_request(ctx, ETHTOOL_MSG_LINKSTATE_GET, + ETHTOOL_A_LINKSTATE_HEADER, linkstate_reply_cb); + if (ret == -ENODEV) + return ret; + + ret = gset_request(ctx, ETHTOOL_MSG_PLCA_GET_STATUS, + ETHTOOL_A_PLCA_HEADER, plca_status_reply_cb); + if (ret == -ENODEV) + return ret; + + if (!ctx->nlctx->no_banner) { + printf("No data available\n"); + return 75; + } + + return 0; +} + +/* SET_SETTINGS */ + +enum { + WAKE_PHY_BIT = 0, + WAKE_UCAST_BIT = 1, + WAKE_MCAST_BIT = 2, + WAKE_BCAST_BIT = 3, + WAKE_ARP_BIT = 4, + WAKE_MAGIC_BIT = 5, + WAKE_MAGICSECURE_BIT = 6, + WAKE_FILTER_BIT = 7, +}; + +#define WAKE_ALL (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP | \ + WAKE_MAGIC | WAKE_MAGICSECURE) + +static const struct lookup_entry_u8 port_values[] = { + { .arg = "tp", .val = PORT_TP }, + { .arg = "aui", .val = PORT_AUI }, + { .arg = "mii", .val = PORT_MII }, + { .arg = "fibre", .val = PORT_FIBRE }, + { .arg = "bnc", .val = PORT_BNC }, + { .arg = "da", .val = PORT_DA }, + {} +}; + +static const struct lookup_entry_u8 mdix_values[] = { + { .arg = "auto", .val = ETH_TP_MDI_AUTO }, + { .arg = "on", .val = ETH_TP_MDI_X }, + { .arg = "off", .val = ETH_TP_MDI }, + {} +}; + +static const struct error_parser_data xcvr_parser_data = { + .err_msg = "deprecated parameter '%s' not supported by kernel\n", + .ret_val = -EINVAL, + .extra_args = 1, +}; + +static const struct lookup_entry_u8 autoneg_values[] = { + { .arg = "off", .val = AUTONEG_DISABLE }, + { .arg = "on", .val = AUTONEG_ENABLE }, + {} +}; + +static const struct bitset_parser_data advertise_parser_data = { + .no_mask = false, + .force_hex = true, +}; + +static const struct lookup_entry_u8 duplex_values[] = { + { .arg = "half", .val = DUPLEX_HALF }, + { .arg = "full", .val = DUPLEX_FULL }, + {} +}; + +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', + [WAKE_MCAST_BIT] = 'm', + [WAKE_BCAST_BIT] = 'b', + [WAKE_ARP_BIT] = 'a', + [WAKE_MAGIC_BIT] = 'g', + [WAKE_MAGICSECURE_BIT] = 's', + [WAKE_FILTER_BIT] = 'f', +}; + +const struct char_bitset_parser_data wol_parser_data = { + .bit_chars = wol_bit_chars, + .nbits = WOL_MODE_COUNT, + .reset_char = 'd', +}; + +const struct byte_str_parser_data sopass_parser_data = { + .min_len = 6, + .max_len = 6, + .delim = ':', +}; + +static const struct bitset_parser_data msglvl_parser_data = { + .no_mask = false, + .force_hex = false, +}; + +static const struct param_parser sset_params[] = { + { + .arg = "port", + .group = ETHTOOL_MSG_LINKINFO_SET, + .type = ETHTOOL_A_LINKINFO_PORT, + .handler = nl_parse_lookup_u8, + .handler_data = port_values, + .min_argc = 1, + }, + { + .arg = "mdix", + .group = ETHTOOL_MSG_LINKINFO_SET, + .type = ETHTOOL_A_LINKINFO_TP_MDIX_CTRL, + .handler = nl_parse_lookup_u8, + .handler_data = mdix_values, + .min_argc = 1, + }, + { + .arg = "phyad", + .group = ETHTOOL_MSG_LINKINFO_SET, + .type = ETHTOOL_A_LINKINFO_PHYADDR, + .handler = nl_parse_direct_u8, + .min_argc = 1, + }, + { + .arg = "xcvr", + .group = ETHTOOL_MSG_LINKINFO_SET, + .handler = nl_parse_error, + .handler_data = &xcvr_parser_data, + .min_argc = 1, + }, + { + .arg = "autoneg", + .group = ETHTOOL_MSG_LINKMODES_SET, + .type = ETHTOOL_A_LINKMODES_AUTONEG, + .handler = nl_parse_lookup_u8, + .handler_data = autoneg_values, + .min_argc = 1, + }, + { + .arg = "advertise", + .group = ETHTOOL_MSG_LINKMODES_SET, + .type = ETHTOOL_A_LINKMODES_OURS, + .handler = nl_parse_bitset, + .handler_data = &advertise_parser_data, + .min_argc = 1, + }, + { + .arg = "speed", + .group = ETHTOOL_MSG_LINKMODES_SET, + .type = ETHTOOL_A_LINKMODES_SPEED, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "lanes", + .group = ETHTOOL_MSG_LINKMODES_SET, + .type = ETHTOOL_A_LINKMODES_LANES, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "duplex", + .group = ETHTOOL_MSG_LINKMODES_SET, + .type = ETHTOOL_A_LINKMODES_DUPLEX, + .handler = nl_parse_lookup_u8, + .handler_data = duplex_values, + .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, + .handler = nl_parse_char_bitset, + .handler_data = &wol_parser_data, + .min_argc = 1, + }, + { + .arg = "sopass", + .group = ETHTOOL_MSG_WOL_SET, + .type = ETHTOOL_A_WOL_SOPASS, + .handler = nl_parse_byte_str, + .handler_data = &sopass_parser_data, + .min_argc = 1, + }, + { + .arg = "msglvl", + .group = ETHTOOL_MSG_DEBUG_SET, + .type = ETHTOOL_A_DEBUG_MSGMASK, + .handler = nl_parse_bitset, + .handler_data = &msglvl_parser_data, + .min_argc = 1, + }, + {} +}; + +/* 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", "duplex" and "lanes", 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] || + tb[ETHTOOL_A_LINKMODES_LANES]) + 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"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + + ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG, msgbuffs); + if (ret == -EOPNOTSUPP) + return ret; + + if (ret < 0) { + ret = 1; + goto out_free; + } + + 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/stats.c b/netlink/stats.c new file mode 100644 index 0000000..8620d8d --- /dev/null +++ b/netlink/stats.c @@ -0,0 +1,333 @@ +/* + * stats.c - netlink implementation of stats + * + * Implementation of "ethtool -S <dev> [--groups <types>] etc." + */ + +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" +#include "strset.h" + +static int parse_rmon_hist_one(const char *grp_name, const struct nlattr *hist, + const char *dir) +{ + const struct nlattr *tb[ETHTOOL_A_STATS_GRP_HIST_VAL + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + unsigned long long val; + unsigned int low, hi; + int ret; + + ret = mnl_attr_parse_nested(hist, attr_cb, &tb_info); + if (ret < 0) { + fprintf(stderr, "invalid kernel response - malformed histogram entry\n"); + return 1; + } + + if (!tb[ETHTOOL_A_STATS_GRP_HIST_BKT_LOW] || + !tb[ETHTOOL_A_STATS_GRP_HIST_BKT_HI] || + !tb[ETHTOOL_A_STATS_GRP_HIST_VAL]) { + fprintf(stderr, "invalid kernel response - histogram entry missing attributes\n"); + return 1; + } + + low = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_HIST_BKT_LOW]); + hi = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_HIST_BKT_HI]); + val = mnl_attr_get_u64(tb[ETHTOOL_A_STATS_GRP_HIST_VAL]); + + if (!is_json_context()) { + fprintf(stdout, "%s-%s-etherStatsPkts", dir, grp_name); + + if (low && hi) { + fprintf(stdout, "%uto%uOctets: %llu\n", low, hi, val); + } else if (hi) { + fprintf(stdout, "%uOctets: %llu\n", hi, val); + } else if (low) { + fprintf(stdout, "%utoMaxOctets: %llu\n", low, val); + } else { + fprintf(stderr, "invalid kernel response - bad histogram entry bounds\n"); + return 1; + } + } else { + open_json_object(NULL); + print_uint(PRINT_JSON, "low", NULL, low); + print_uint(PRINT_JSON, "high", NULL, hi); + print_u64(PRINT_JSON, "val", NULL, val); + close_json_object(); + } + + return 0; +} + +static int parse_rmon_hist(const struct nlattr *grp, const char *grp_name, + const char *name, const char *dir, unsigned int type) +{ + const struct nlattr *attr; + + open_json_array(name, ""); + + mnl_attr_for_each_nested(attr, grp) { + if (mnl_attr_get_type(attr) == type && + parse_rmon_hist_one(grp_name, attr, dir)) + goto err_close_rmon; + } + close_json_array(""); + + return 0; + +err_close_rmon: + close_json_array(""); + return 1; +} + +static int parse_grp(struct nl_context *nlctx, const struct nlattr *grp, + const struct stringset *std_str) +{ + const struct nlattr *tb[ETHTOOL_A_STATS_GRP_SS_ID + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + bool hist_rx = false, hist_tx = false; + const struct stringset *stat_str; + const struct nlattr *attr, *stat; + const char *std_name, *name; + unsigned int ss_id, id, s; + unsigned long long val; + int ret; + + ret = mnl_attr_parse_nested(grp, attr_cb, &tb_info); + if (ret < 0) + return 1; + + if (!tb[ETHTOOL_A_STATS_GRP_ID]) + return 1; + if (!tb[ETHTOOL_A_STATS_GRP_SS_ID]) + return 0; + + id = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_ID]); + ss_id = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_SS_ID]); + + stat_str = global_stringset(ss_id, nlctx->ethnl2_socket); + + std_name = get_string(std_str, id); + open_json_object(std_name); + + mnl_attr_for_each_nested(attr, grp) { + switch (mnl_attr_get_type(attr)) { + case ETHTOOL_A_STATS_GRP_STAT: + break; + case ETHTOOL_A_STATS_GRP_HIST_RX: + hist_rx = true; + continue; + case ETHTOOL_A_STATS_GRP_HIST_TX: + hist_tx = true; + continue; + default: + continue; + } + + stat = mnl_attr_get_payload(attr); + ret = mnl_attr_validate(stat, MNL_TYPE_U64); + if (ret) { + fprintf(stderr, "invalid kernel response - bad statistic entry\n"); + goto err_close_grp; + } + s = mnl_attr_get_type(stat); + name = get_string(stat_str, s); + if (!name || !name[0]) + continue; + + if (!is_json_context()) + fprintf(stdout, "%s-%s: ", std_name, name); + + val = mnl_attr_get_u64(stat); + print_u64(PRINT_ANY, name, "%llu\n", val); + } + + if (hist_rx) + parse_rmon_hist(grp, std_name, "rx-pktsNtoM", "rx", + ETHTOOL_A_STATS_GRP_HIST_RX); + if (hist_tx) + parse_rmon_hist(grp, std_name, "tx-pktsNtoM", "tx", + ETHTOOL_A_STATS_GRP_HIST_TX); + + close_json_object(); + + return 0; + +err_close_grp: + close_json_object(); + return 1; +} + +static int stats_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_STATS_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + const struct stringset *std_str; + const struct nlattr *attr; + 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_STATS_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return err_ret; + std_str = global_stringset(ETH_SS_STATS_STD, nlctx->ethnl2_socket); + + if (silent) + print_nl(); + + open_json_object(NULL); + + print_string(PRINT_ANY, "ifname", "Standard stats for %s:\n", + nlctx->devname); + + mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) { + if (mnl_attr_get_type(attr) == ETHTOOL_A_STATS_GRP) { + ret = parse_grp(nlctx, attr, std_str); + if (ret) + goto err_close_dev; + } + } + + close_json_object(); + + return MNL_CB_OK; + +err_close_dev: + close_json_object(); + return err_ret; +} + +static const struct bitset_parser_data stats_parser_data = { + .no_mask = true, + .force_hex = false, +}; + +static int stats_parse_all_groups(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest) +{ + const struct stringset *std_str; + struct nlattr *nest; + int i, ret, nbits; + uint32_t *bits; + + if (data || dest) + return -EFAULT; + + /* ethnl2 and strset code already does caching */ + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return ret; + std_str = global_stringset(ETH_SS_STATS_STD, nlctx->ethnl2_socket); + + nbits = get_count(std_str); + bits = calloc(DIV_ROUND_UP(nbits, 32), sizeof(uint32_t)); + if (!bits) + return -ENOMEM; + + for (i = 0; i < nbits; i++) + bits[i / 32] |= 1U << (i % 32); + + ret = -EMSGSIZE; + nest = ethnla_nest_start(msgbuff, type); + if (!nest) + goto err_free; + + if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true) || + ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_SIZE, nbits) || + ethnla_put(msgbuff, ETHTOOL_A_BITSET_VALUE, + DIV_ROUND_UP(nbits, 32) * sizeof(uint32_t), bits)) + goto err_cancel; + + ethnla_nest_end(msgbuff, nest); + free(bits); + return 0; + +err_cancel: + ethnla_nest_cancel(msgbuff, nest); +err_free: + free(bits); + return ret; +} + +static const struct lookup_entry_u32 stats_src_values[] = { + { .arg = "aggregate", .val = ETHTOOL_MAC_STATS_SRC_AGGREGATE }, + { .arg = "emac", .val = ETHTOOL_MAC_STATS_SRC_EMAC }, + { .arg = "pmac", .val = ETHTOOL_MAC_STATS_SRC_PMAC }, + {} +}; + +static const struct param_parser stats_params[] = { + { + .arg = "--groups", + .type = ETHTOOL_A_STATS_GROUPS, + .handler = nl_parse_bitset, + .handler_data = &stats_parser_data, + .min_argc = 1, + .alt_group = 1, + }, + { + .arg = "--all-groups", + .type = ETHTOOL_A_STATS_GROUPS, + .handler = stats_parse_all_groups, + .alt_group = 1, + }, + { + .arg = "--src", + .type = ETHTOOL_A_STATS_SRC, + .handler = nl_parse_lookup_u32, + .handler_data = stats_src_values, + .min_argc = 1, + }, + {} +}; + +int nl_gstats(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_STATS_GET, + ETHTOOL_A_STATS_HEADER, 0); + if (ret < 0) + return ret; + + nlctx->cmd = "-S"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + + ret = nl_parser(nlctx, stats_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, stats_reply_cb); + delete_json_obj(); + return ret; +} + +bool nl_gstats_chk(struct cmd_context *ctx) +{ + return ctx->argc; +} diff --git a/netlink/strset.c b/netlink/strset.c new file mode 100644 index 0000000..fbc9c17 --- /dev/null +++ b/netlink/strset.c @@ -0,0 +1,297 @@ +/* + * strset.c - string set handling + * + * Implementation of local cache of ethtool string sets. + */ + +#include <errno.h> +#include <string.h> + +#include "../internal.h" +#include "netlink.h" +#include "nlsock.h" +#include "msgbuff.h" + +struct stringset { + const char **strings; + void *raw_data; + unsigned int count; + bool loaded; +}; + +struct perdev_strings { + int ifindex; + char devname[ALTIFNAMSIZ]; + struct stringset strings[ETH_SS_COUNT]; + struct perdev_strings *next; +}; + +/* universal string sets */ +static struct stringset global_strings[ETH_SS_COUNT]; +/* linked list of string sets related to network devices */ +static struct perdev_strings *device_strings; + +static void drop_stringset(struct stringset *set) +{ + if (!set) + return; + + free(set->strings); + free(set->raw_data); + memset(set, 0, sizeof(*set)); +} + +static int import_stringset(struct stringset *dest, const struct nlattr *nest) +{ + const struct nlattr *tb_stringset[ETHTOOL_A_STRINGSET_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb_stringset); + const struct nlattr *string; + unsigned int size; + unsigned int count; + unsigned int idx; + int ret; + + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_stringset_info); + if (ret < 0) + return ret; + if (!tb_stringset[ETHTOOL_A_STRINGSET_ID] || + !tb_stringset[ETHTOOL_A_STRINGSET_COUNT] || + !tb_stringset[ETHTOOL_A_STRINGSET_STRINGS]) + return -EFAULT; + idx = mnl_attr_get_u32(tb_stringset[ETHTOOL_A_STRINGSET_ID]); + if (idx >= ETH_SS_COUNT) + return 0; + if (dest[idx].loaded) + drop_stringset(&dest[idx]); + dest[idx].loaded = true; + count = mnl_attr_get_u32(tb_stringset[ETHTOOL_A_STRINGSET_COUNT]); + if (count == 0) + return 0; + + size = mnl_attr_get_len(tb_stringset[ETHTOOL_A_STRINGSET_STRINGS]); + ret = -ENOMEM; + dest[idx].raw_data = malloc(size); + if (!dest[idx].raw_data) + goto err; + memcpy(dest[idx].raw_data, tb_stringset[ETHTOOL_A_STRINGSET_STRINGS], + size); + dest[idx].strings = calloc(count, sizeof(dest[idx].strings[0])); + if (!dest[idx].strings) + goto err; + dest[idx].count = count; + + nest = dest[idx].raw_data; + mnl_attr_for_each_nested(string, nest) { + const struct nlattr *tb[ETHTOOL_A_STRING_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + unsigned int i; + + if (mnl_attr_get_type(string) != ETHTOOL_A_STRINGS_STRING) + continue; + ret = mnl_attr_parse_nested(string, attr_cb, &tb_info); + if (ret < 0) + goto err; + ret = -EFAULT; + if (!tb[ETHTOOL_A_STRING_INDEX] || !tb[ETHTOOL_A_STRING_VALUE]) + goto err; + + i = mnl_attr_get_u32(tb[ETHTOOL_A_STRING_INDEX]); + if (i >= count) + goto err; + dest[idx].strings[i] = + mnl_attr_get_payload(tb[ETHTOOL_A_STRING_VALUE]); + } + + return 0; +err: + drop_stringset(&dest[idx]); + return ret; +} + +static struct perdev_strings *get_perdev_by_ifindex(int ifindex) +{ + struct perdev_strings *perdev = device_strings; + + while (perdev && perdev->ifindex != ifindex) + perdev = perdev->next; + if (perdev) + return perdev; + + /* not found, allocate and insert into list */ + perdev = calloc(sizeof(*perdev), 1); + if (!perdev) + return NULL; + perdev->ifindex = ifindex; + perdev->next = device_strings; + device_strings = perdev; + + return perdev; +} + +static int strset_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_STRSET_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + char devname[ALTIFNAMSIZ] = ""; + struct stringset *dest; + struct nlattr *attr; + int ifindex = 0; + int ret; + + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + if (tb[ETHTOOL_A_STRSET_HEADER]) { + ret = get_dev_info(tb[ETHTOOL_A_STRSET_HEADER], &ifindex, + devname); + if (ret < 0) + return MNL_CB_OK; + if (ifindex && nlctx->filter_devname && + strncmp(devname, nlctx->filter_devname, ALTIFNAMSIZ)) + return MNL_CB_OK; + } + + if (ifindex) { + struct perdev_strings *perdev; + + perdev = get_perdev_by_ifindex(ifindex); + if (!perdev) + return MNL_CB_OK; + copy_devname(perdev->devname, devname); + dest = perdev->strings; + } else { + dest = global_strings; + } + + if (!tb[ETHTOOL_A_STRSET_STRINGSETS]) + return MNL_CB_OK; + mnl_attr_for_each_nested(attr, tb[ETHTOOL_A_STRSET_STRINGSETS]) { + if (mnl_attr_get_type(attr) == + ETHTOOL_A_STRINGSETS_STRINGSET) + import_stringset(dest, attr); + } + + return MNL_CB_OK; +} + +static int fill_stringset_id(struct nl_msg_buff *msgbuff, unsigned int type) +{ + struct nlattr *nest_sets; + struct nlattr *nest_set; + + nest_sets = ethnla_nest_start(msgbuff, ETHTOOL_A_STRSET_STRINGSETS); + if (!nest_sets) + return -EMSGSIZE; + nest_set = ethnla_nest_start(msgbuff, ETHTOOL_A_STRINGSETS_STRINGSET); + if (!nest_set) + goto err; + if (ethnla_put_u32(msgbuff, ETHTOOL_A_STRINGSET_ID, type)) + goto err; + ethnla_nest_end(msgbuff, nest_set); + ethnla_nest_end(msgbuff, nest_sets); + return 0; + +err: + ethnla_nest_cancel(msgbuff, nest_sets); + return -EMSGSIZE; +} + +static int stringset_load_request(struct nl_socket *nlsk, const char *devname, + int type, bool is_dump) +{ + struct nl_msg_buff *msgbuff = &nlsk->msgbuff; + int ret; + + ret = msg_init(nlsk->nlctx, msgbuff, ETHTOOL_MSG_STRSET_GET, + NLM_F_REQUEST | NLM_F_ACK | (is_dump ? NLM_F_DUMP : 0)); + if (ret < 0) + return ret; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_STRSET_HEADER, devname, 0)) + return -EMSGSIZE; + if (type >= 0) { + ret = fill_stringset_id(msgbuff, type); + if (ret < 0) + return ret; + } + + ret = nlsock_send_get_request(nlsk, strset_reply_cb); + return ret; +} + +/* interface */ + +const struct stringset *global_stringset(unsigned int type, + struct nl_socket *nlsk) +{ + int ret; + + if (type >= ETH_SS_COUNT) + return NULL; + if (global_strings[type].loaded) + return &global_strings[type]; + ret = stringset_load_request(nlsk, NULL, type, false); + return ret < 0 ? NULL : &global_strings[type]; +} + +const struct stringset *perdev_stringset(const char *devname, unsigned int type, + struct nl_socket *nlsk) +{ + const struct perdev_strings *p; + int ret; + + if (type >= ETH_SS_COUNT) + return NULL; + for (p = device_strings; p; p = p->next) + if (!strcmp(p->devname, devname)) + return &p->strings[type]; + + ret = stringset_load_request(nlsk, devname, type, false); + if (ret < 0) + return NULL; + for (p = device_strings; p; p = p->next) + if (!strcmp(p->devname, devname)) + return &p->strings[type]; + + return NULL; +} + +unsigned int get_count(const struct stringset *set) +{ + return set->count; +} + +const char *get_string(const struct stringset *set, unsigned int idx) +{ + if (!set || idx >= set->count) + return NULL; + return set->strings[idx]; +} + +int preload_global_strings(struct nl_socket *nlsk) +{ + return stringset_load_request(nlsk, NULL, -1, false); +} + +int preload_perdev_strings(struct nl_socket *nlsk, const char *dev) +{ + return stringset_load_request(nlsk, dev, -1, !dev); +} + +void cleanup_all_strings(void) +{ + struct perdev_strings *perdev; + unsigned int i; + + for (i = 0; i < ETH_SS_COUNT; i++) + drop_stringset(&global_strings[i]); + + perdev = device_strings; + while (perdev) { + device_strings = perdev->next; + for (i = 0; i < ETH_SS_COUNT; i++) + drop_stringset(&perdev->strings[i]); + free(perdev); + perdev = device_strings; + } +} diff --git a/netlink/strset.h b/netlink/strset.h new file mode 100644 index 0000000..72a4a39 --- /dev/null +++ b/netlink/strset.h @@ -0,0 +1,25 @@ +/* + * strset.h - string set handling + * + * Interface for local cache of ethtool string sets. + */ + +#ifndef ETHTOOL_NETLINK_STRSET_H__ +#define ETHTOOL_NETLINK_STRSET_H__ + +struct nl_socket; +struct stringset; + +const struct stringset *global_stringset(unsigned int type, + struct nl_socket *nlsk); +const struct stringset *perdev_stringset(const char *dev, unsigned int type, + struct nl_socket *nlsk); + +unsigned int get_count(const struct stringset *set); +const char *get_string(const struct stringset *set, unsigned int idx); + +int preload_global_strings(struct nl_socket *nlsk); +int preload_perdev_strings(struct nl_socket *nlsk, const char *dev); +void cleanup_all_strings(void); + +#endif /* ETHTOOL_NETLINK_STRSET_H__ */ 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); +} @@ -55,15 +55,29 @@ **/ #include <stdio.h> #include <math.h> +#include <errno.h> #include "internal.h" #include "sff-common.h" #include "qsfp.h" +#include "cmis.h" +#include "netlink/extapi.h" + +struct sff8636_memory_map { + const __u8 *lower_memory; + const __u8 *upper_memory[4]; +#define page_00h upper_memory[0x0] +#define page_03h upper_memory[0x3] +}; + +#define SFF8636_PAGE_SIZE 0x80 +#define SFF8636_I2C_ADDRESS 0x50 +#define SFF8636_MAX_CHANNEL_NUM 4 #define MAX_DESC_SIZE 42 static struct sff8636_aw_flags { const char *str; /* Human-readable string, null at the end */ - int offset; /* A2-relative address offset */ + int offset; __u8 value; /* Alarm is on if (offset & value) != 0. */ } sff8636_aw_flags[] = { { "Laser bias current high alarm (Chan 1)", @@ -195,20 +209,21 @@ static struct sff8636_aw_flags { { NULL, 0, 0 }, }; -static void sff8636_show_identifier(const __u8 *id) +static void sff8636_show_identifier(const struct sff8636_memory_map *map) { - sff8024_show_identifier(id, SFF8636_ID_OFFSET); + sff8024_show_identifier(map->lower_memory, SFF8636_ID_OFFSET); } -static void sff8636_show_ext_identifier(const __u8 *id) +static void sff8636_show_ext_identifier(const struct sff8636_memory_map *map) { printf("\t%-41s : 0x%02x\n", "Extended identifier", - id[SFF8636_EXT_ID_OFFSET]); + map->page_00h[SFF8636_EXT_ID_OFFSET]); static const char *pfx = "\tExtended identifier description :"; - switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_PWR_CLASS_MASK) { + switch (map->page_00h[SFF8636_EXT_ID_OFFSET] & + SFF8636_EXT_ID_PWR_CLASS_MASK) { case SFF8636_EXT_ID_PWR_CLASS_1: printf("%s 1.5W max. Power consumption\n", pfx); break; @@ -223,17 +238,18 @@ static void sff8636_show_ext_identifier(const __u8 *id) break; } - if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK) + if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK) printf("%s CDR present in TX,", pfx); else printf("%s No CDR in TX,", pfx); - if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK) + if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK) printf(" CDR present in RX\n"); else printf(" No CDR in RX\n"); - switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_EPWR_CLASS_MASK) { + switch (map->page_00h[SFF8636_EXT_ID_OFFSET] & + SFF8636_EXT_ID_EPWR_CLASS_MASK) { case SFF8636_EXT_ID_PWR_CLASS_LEGACY: printf("%s", pfx); break; @@ -247,18 +263,25 @@ static void sff8636_show_ext_identifier(const __u8 *id) printf("%s 5.0W max. Power consumption, ", pfx); break; } - if (id[SFF8636_PWR_MODE_OFFSET] & SFF8636_HIGH_PWR_ENABLE) + if (map->lower_memory[SFF8636_PWR_MODE_OFFSET] & + SFF8636_HIGH_PWR_ENABLE) printf(" High Power Class (> 3.5 W) enabled\n"); else printf(" High Power Class (> 3.5 W) not enabled\n"); + printf("\t%-41s : ", "Power set"); + printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] & + SFF8636_LOW_PWR_SET)); + printf("\t%-41s : ", "Power override"); + printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] & + SFF8636_PWR_OVERRIDE)); } -static void sff8636_show_connector(const __u8 *id) +static void sff8636_show_connector(const struct sff8636_memory_map *map) { - sff8024_show_connector(id, SFF8636_CTOR_OFFSET); + sff8024_show_connector(map->page_00h, SFF8636_CTOR_OFFSET); } -static void sff8636_show_transceiver(const __u8 *id) +static void sff8636_show_transceiver(const struct sff8636_memory_map *map) { static const char *pfx = "\tTransceiver type :"; @@ -266,33 +289,41 @@ static void sff8636_show_transceiver(const __u8 *id) printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \ "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", "Transceiver codes", - id[SFF8636_ETHERNET_COMP_OFFSET], - id[SFF8636_SONET_COMP_OFFSET], - id[SFF8636_SAS_COMP_OFFSET], - id[SFF8636_GIGE_COMP_OFFSET], - id[SFF8636_FC_LEN_OFFSET], - id[SFF8636_FC_TECH_OFFSET], - id[SFF8636_FC_TRANS_MEDIA_OFFSET], - id[SFF8636_FC_SPEED_OFFSET]); + map->page_00h[SFF8636_ETHERNET_COMP_OFFSET], + map->page_00h[SFF8636_SONET_COMP_OFFSET], + map->page_00h[SFF8636_SAS_COMP_OFFSET], + map->page_00h[SFF8636_GIGE_COMP_OFFSET], + map->page_00h[SFF8636_FC_LEN_OFFSET], + map->page_00h[SFF8636_FC_TECH_OFFSET], + map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET], + map->page_00h[SFF8636_FC_SPEED_OFFSET]); /* 10G/40G Ethernet Compliance Codes */ - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LRM) + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_10G_LRM) printf("%s 10G Ethernet: 10G Base-LRM\n", pfx); - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LR) + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_10G_LR) printf("%s 10G Ethernet: 10G Base-LR\n", pfx); - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_SR) + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_10G_SR) printf("%s 10G Ethernet: 10G Base-SR\n", pfx); - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_CR4) + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_40G_CR4) printf("%s 40G Ethernet: 40G Base-CR4\n", pfx); - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_SR4) + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_40G_SR4) printf("%s 40G Ethernet: 40G Base-SR4\n", pfx); - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_LR4) + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_40G_LR4) printf("%s 40G Ethernet: 40G Base-LR4\n", pfx); - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_ACTIVE) + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_40G_ACTIVE) printf("%s 40G Ethernet: 40G Active Cable (XLPPI)\n", pfx); /* Extended Specification Compliance Codes from SFF-8024 */ - if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_RSRVD) { - switch (id[SFF8636_OPTION_1_OFFSET]) { + if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & + SFF8636_ETHERNET_RSRVD) { + switch (map->page_00h[SFF8636_OPTION_1_OFFSET]) { case SFF8636_ETHERNET_UNSPECIFIED: printf("%s (reserved or unknown)\n", pfx); break; @@ -375,6 +406,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; @@ -382,113 +514,122 @@ static void sff8636_show_transceiver(const __u8 *id) } /* SONET Compliance Codes */ - if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_40G_OTN)) + if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & + (SFF8636_SONET_40G_OTN)) printf("%s 40G OTN (OTU3B/OTU3C)\n", pfx); - if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR)) + if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR)) printf("%s SONET: OC-48, long reach\n", pfx); - if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR)) + if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR)) printf("%s SONET: OC-48, intermediate reach\n", pfx); - if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR)) + if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR)) printf("%s SONET: OC-48, short reach\n", pfx); /* SAS/SATA Compliance Codes */ - if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G)) + if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G)) printf("%s SAS 6.0G\n", pfx); - if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G)) + if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G)) printf("%s SAS 3.0G\n", pfx); /* Ethernet Compliance Codes */ - if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T) + if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T) printf("%s Ethernet: 1000BASE-T\n", pfx); - if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX) + if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX) printf("%s Ethernet: 1000BASE-CX\n", pfx); - if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX) + if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX) printf("%s Ethernet: 1000BASE-LX\n", pfx); - if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX) + if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX) printf("%s Ethernet: 1000BASE-SX\n", pfx); /* Fibre Channel link length */ - if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG) + if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG) printf("%s FC: very long distance (V)\n", pfx); - if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT) + if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT) printf("%s FC: short distance (S)\n", pfx); - if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT) + if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT) printf("%s FC: intermediate distance (I)\n", pfx); - if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG) + if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG) printf("%s FC: long distance (L)\n", pfx); - if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED) + if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED) printf("%s FC: medium distance (M)\n", pfx); /* Fibre Channel transmitter technology */ - if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC) + if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC) printf("%s FC: Longwave laser (LC)\n", pfx); - if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER) + if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER) printf("%s FC: Electrical inter-enclosure (EL)\n", pfx); - if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA) + if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA) printf("%s FC: Electrical intra-enclosure (EL)\n", pfx); - if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_WO_OFC) + if (map->page_00h[SFF8636_FC_TECH_OFFSET] & + SFF8636_FC_TECH_SHORT_WO_OFC) printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx); - if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC) + if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC) printf("%s FC: Shortwave laser with OFC (SL)\n", pfx); - if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL) + if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL) printf("%s FC: Longwave laser (LL)\n", pfx); /* Fibre Channel transmission media */ - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TW) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_TW) printf("%s FC: Twin Axial Pair (TW)\n", pfx); - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TP) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_TP) printf("%s FC: Twisted Pair (TP)\n", pfx); - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_MI) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_MI) printf("%s FC: Miniature Coax (MI)\n", pfx); - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TV) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_TV) printf("%s FC: Video Coax (TV)\n", pfx); - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M6) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_M6) printf("%s FC: Multimode, 62.5m (M6)\n", pfx); - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M5) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_M5) printf("%s FC: Multimode, 50m (M5)\n", pfx); - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_OM3) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_OM3) printf("%s FC: Multimode, 50um (OM3)\n", pfx); - if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_SM) + if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & + SFF8636_FC_TRANS_MEDIA_SM) printf("%s FC: Single Mode (SM)\n", pfx); /* Fibre Channel speed */ - if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS) + if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS) printf("%s FC: 1200 MBytes/sec\n", pfx); - if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS) + if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS) printf("%s FC: 800 MBytes/sec\n", pfx); - if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS) + if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS) printf("%s FC: 1600 MBytes/sec\n", pfx); - if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS) + if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS) printf("%s FC: 400 MBytes/sec\n", pfx); - if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS) + if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS) printf("%s FC: 200 MBytes/sec\n", pfx); - if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS) + if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS) printf("%s FC: 100 MBytes/sec\n", pfx); } -static void sff8636_show_encoding(const __u8 *id) +static void sff8636_show_encoding(const struct sff8636_memory_map *map) { - sff8024_show_encoding(id, SFF8636_ENCODING_OFFSET, ETH_MODULE_SFF_8636); + sff8024_show_encoding(map->page_00h, SFF8636_ENCODING_OFFSET, + ETH_MODULE_SFF_8636); } -static void sff8636_show_rate_identifier(const __u8 *id) +static void sff8636_show_rate_identifier(const struct sff8636_memory_map *map) { /* TODO: Need to fix rate select logic */ printf("\t%-41s : 0x%02x\n", "Rate identifier", - id[SFF8636_EXT_RS_OFFSET]); + map->page_00h[SFF8636_EXT_RS_OFFSET]); } -static void sff8636_show_oui(const __u8 *id) -{ - sff8024_show_oui(id, SFF8636_VENDOR_OUI_OFFSET); -} - -static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id) +static void +sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *map) { printf("\t%-41s : 0x%02x", "Transmitter technology", - (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK)); + map->page_00h[SFF8636_DEVICE_TECH_OFFSET] & + SFF8636_TRANS_TECH_MASK); - switch (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) { + switch (map->page_00h[SFF8636_DEVICE_TECH_OFFSET] & + SFF8636_TRANS_TECH_MASK) { case SFF8636_TRANS_850_VCSEL: printf(" (850 nm VCSEL)\n"); break; @@ -539,59 +680,23 @@ static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id) break; } - if ((id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) - >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) { + if ((map->page_00h[SFF8636_DEVICE_TECH_OFFSET] & + SFF8636_TRANS_TECH_MASK) >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) { printf("\t%-41s : %udb\n", "Attenuation at 2.5GHz", - id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]); + map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]); printf("\t%-41s : %udb\n", "Attenuation at 5.0GHz", - id[SFF8636_WAVELEN_LOW_BYTE_OFFSET]); + map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]); printf("\t%-41s : %udb\n", "Attenuation at 7.0GHz", - id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]); + map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]); printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz", - id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]); + map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]); } else { printf("\t%-41s : %.3lfnm\n", "Laser wavelength", - (((id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) | - id[SFF8636_WAVELEN_LOW_BYTE_OFFSET])*0.05)); + (((map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) | + map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]) * 0.05)); printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance", - (((id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) | - id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET])*0.005)); - } -} - -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; + (((map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) | + map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]) * 0.005)); } } @@ -602,41 +707,67 @@ static void sff8636_show_revision_compliance(const __u8 *id) */ #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset)) -static void sff8636_dom_parse(const __u8 *id, struct sff_diags *sd) +static void sff8636_dom_parse(const struct sff8636_memory_map *map, + struct sff_diags *sd) { + const __u8 *id = map->lower_memory; int i = 0; /* Monitoring Thresholds for Alarms and Warnings */ - sd->sfp_voltage[MCURR] = OFFSET_TO_U16(SFF8636_VCC_CURR); - sd->sfp_voltage[HALRM] = OFFSET_TO_U16(SFF8636_VCC_HALRM); - sd->sfp_voltage[LALRM] = OFFSET_TO_U16(SFF8636_VCC_LALRM); - sd->sfp_voltage[HWARN] = OFFSET_TO_U16(SFF8636_VCC_HWARN); - sd->sfp_voltage[LWARN] = OFFSET_TO_U16(SFF8636_VCC_LWARN); - + sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(id, SFF8636_VCC_CURR); sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR); - sd->sfp_temp[HALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HALRM); - sd->sfp_temp[LALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LALRM); - sd->sfp_temp[HWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HWARN); - sd->sfp_temp[LWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LWARN); - - sd->bias_cur[HALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_HALRM); - sd->bias_cur[LALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_LALRM); - sd->bias_cur[HWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_HWARN); - sd->bias_cur[LWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_LWARN); - - sd->tx_power[HALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_HALRM); - sd->tx_power[LALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_LALRM); - sd->tx_power[HWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_HWARN); - sd->tx_power[LWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_LWARN); - - sd->rx_power[HALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_HALRM); - sd->rx_power[LALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_LALRM); - sd->rx_power[HWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_HWARN); - sd->rx_power[LWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_LWARN); - + if (!map->page_03h) + goto out; + + sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_VCC_HALRM); + sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_VCC_LALRM); + sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_VCC_HWARN); + sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_VCC_LWARN); + + sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TEMP_HALRM); + sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TEMP_LALRM); + sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TEMP_HWARN); + sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TEMP_LWARN); + + sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_BIAS_HALRM); + sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_BIAS_LALRM); + sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_BIAS_HWARN); + sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_BIAS_LWARN); + + sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_PWR_HALRM); + sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_PWR_LALRM); + sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_PWR_HWARN); + sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_TX_PWR_LWARN); + + sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_RX_PWR_HALRM); + sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_RX_PWR_LALRM); + sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_RX_PWR_HWARN); + sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, + SFF8636_RX_PWR_LWARN); + +out: /* Channel Specific Data */ - for (i = 0; i < MAX_CHANNEL_NUM; i++) { + for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { u8 rx_power_offset, tx_bias_offset; u8 tx_power_offset; @@ -666,10 +797,9 @@ static void sff8636_dom_parse(const __u8 *id, struct sff_diags *sd) sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset); sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset); } - } -static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len) +static void sff8636_show_dom(const struct sff8636_memory_map *map) { struct sff_diags sd = {0}; char *rx_power_string = NULL; @@ -683,20 +813,15 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len) * and thresholds * If pagging support exists, then supports_alarms is marked as 1 */ + if (map->page_03h) + sd.supports_alarms = 1; - if (eeprom_len == ETH_MODULE_SFF_8636_MAX_LEN) { - if (!(id[SFF8636_STATUS_2_OFFSET] & - SFF8636_STATUS_PAGE_3_PRESENT)) { - sd.supports_alarms = 1; - } - } - - sd.rx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] & - SFF8636_RX_PWR_TYPE_MASK; - sd.tx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] & - SFF8636_RX_PWR_TYPE_MASK; + sd.rx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] & + SFF8636_RX_PWR_TYPE_MASK; + sd.tx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] & + SFF8636_RX_PWR_TYPE_MASK; - sff8636_dom_parse(id, &sd); + sff8636_dom_parse(map, &sd); PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]); PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]); @@ -713,13 +838,13 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len) printf("\t%-41s : %s\n", "Alarm/warning flags implemented", (sd.supports_alarms ? "Yes" : "No")); - for (i = 0; i < MAX_CHANNEL_NUM; i++) { + for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)", "Laser tx bias current", i+1); PRINT_BIAS(power_string, sd.scd[i].bias_cur); } - for (i = 0; i < MAX_CHANNEL_NUM; i++) { + for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)", "Transmit avg optical power", i+1); PRINT_xX_PWR(power_string, sd.scd[i].tx_power); @@ -730,7 +855,7 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len) else rx_power_string = "Rcvr signal avg optical power"; - for (i = 0; i < MAX_CHANNEL_NUM; i++) { + for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { snprintf(power_string, MAX_DESC_SIZE, "%s(Channel %d)", rx_power_string, i+1); PRINT_xX_PWR(power_string, sd.scd[i].rx_power); @@ -739,50 +864,191 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len) if (sd.supports_alarms) { for (i = 0; sff8636_aw_flags[i].str; ++i) { printf("\t%-41s : %s\n", sff8636_aw_flags[i].str, - id[sff8636_aw_flags[i].offset] + map->lower_memory[sff8636_aw_flags[i].offset] & sff8636_aw_flags[i].value ? "On" : "Off"); } sff_show_thresholds(sd); } +} + +static void sff8636_show_signals(const struct sff8636_memory_map *map) +{ + unsigned int v; + + /* There appears to be no Rx LOS support bit, use Tx for both */ + if (map->page_00h[SFF8636_OPTION_4_OFFSET] & SFF8636_O4_TX_LOS) { + v = map->lower_memory[SFF8636_LOS_AW_OFFSET] & 0xf; + sff_show_lane_status("Rx loss of signal", 4, "Yes", "No", v); + v = map->lower_memory[SFF8636_LOS_AW_OFFSET] >> 4; + sff_show_lane_status("Tx loss of signal", 4, "Yes", "No", v); + } + + v = map->lower_memory[SFF8636_LOL_AW_OFFSET] & 0xf; + if (map->page_00h[SFF8636_OPTION_3_OFFSET] & SFF8636_O3_RX_LOL) + sff_show_lane_status("Rx loss of lock", 4, "Yes", "No", v); + + v = map->lower_memory[SFF8636_LOL_AW_OFFSET] >> 4; + if (map->page_00h[SFF8636_OPTION_3_OFFSET] & SFF8636_O3_TX_LOL) + sff_show_lane_status("Tx loss of lock", 4, "Yes", "No", v); + + v = map->lower_memory[SFF8636_FAULT_AW_OFFSET] & 0xf; + if (map->page_00h[SFF8636_OPTION_4_OFFSET] & SFF8636_O4_TX_FAULT) + sff_show_lane_status("Tx fault", 4, "Yes", "No", v); + + v = map->lower_memory[SFF8636_FAULT_AW_OFFSET] >> 4; + if (map->page_00h[SFF8636_OPTION_2_OFFSET] & SFF8636_O2_TX_EQ_AUTO) + sff_show_lane_status("Tx adaptive eq fault", 4, "Yes", "No", v); +} +static void sff8636_show_page_zero(const struct sff8636_memory_map *map) +{ + sff8636_show_ext_identifier(map); + sff8636_show_connector(map); + sff8636_show_transceiver(map); + sff8636_show_encoding(map); + sff_show_value_with_unit(map->page_00h, SFF8636_BR_NOMINAL_OFFSET, + "BR, Nominal", 100, "Mbps"); + sff8636_show_rate_identifier(map); + sff_show_value_with_unit(map->page_00h, SFF8636_SM_LEN_OFFSET, + "Length (SMF,km)", 1, "km"); + sff_show_value_with_unit(map->page_00h, SFF8636_OM3_LEN_OFFSET, + "Length (OM3 50um)", 2, "m"); + sff_show_value_with_unit(map->page_00h, SFF8636_OM2_LEN_OFFSET, + "Length (OM2 50um)", 1, "m"); + sff_show_value_with_unit(map->page_00h, SFF8636_OM1_LEN_OFFSET, + "Length (OM1 62.5um)", 1, "m"); + sff_show_value_with_unit(map->page_00h, SFF8636_CBL_LEN_OFFSET, + "Length (Copper or Active cable)", 1, "m"); + sff8636_show_wavelength_or_copper_compliance(map); + sff_show_ascii(map->page_00h, SFF8636_VENDOR_NAME_START_OFFSET, + SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name"); + sff8024_show_oui(map->page_00h, SFF8636_VENDOR_OUI_OFFSET); + sff_show_ascii(map->page_00h, SFF8636_VENDOR_PN_START_OFFSET, + SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN"); + sff_show_ascii(map->page_00h, SFF8636_VENDOR_REV_START_OFFSET, + SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev"); + sff_show_ascii(map->page_00h, SFF8636_VENDOR_SN_START_OFFSET, + SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN"); + sff_show_ascii(map->page_00h, SFF8636_DATE_YEAR_OFFSET, + SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); + sff_show_revision_compliance(map->lower_memory, + SFF8636_REV_COMPLIANCE_OFFSET); + sff8636_show_signals(map); } -void sff8636_show_all(const __u8 *id, __u32 eeprom_len) + +static void sff8636_show_all_common(const struct sff8636_memory_map *map) { - sff8636_show_identifier(id); - if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) || - (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) || - (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) { - sff8636_show_ext_identifier(id); - sff8636_show_connector(id); - sff8636_show_transceiver(id); - sff8636_show_encoding(id); - sff_show_value_with_unit(id, SFF8636_BR_NOMINAL_OFFSET, - "BR, Nominal", 100, "Mbps"); - sff8636_show_rate_identifier(id); - sff_show_value_with_unit(id, SFF8636_SM_LEN_OFFSET, - "Length (SMF,km)", 1, "km"); - sff_show_value_with_unit(id, SFF8636_OM3_LEN_OFFSET, - "Length (OM3 50um)", 2, "m"); - sff_show_value_with_unit(id, SFF8636_OM2_LEN_OFFSET, - "Length (OM2 50um)", 1, "m"); - sff_show_value_with_unit(id, SFF8636_OM1_LEN_OFFSET, - "Length (OM1 62.5um)", 1, "m"); - sff_show_value_with_unit(id, SFF8636_CBL_LEN_OFFSET, - "Length (Copper or Active cable)", 1, "m"); - 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); - 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, - SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev"); - sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET, - SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN"); - sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET, - SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); - sff8636_show_revision_compliance(id); - sff8636_show_dom(id, eeprom_len); + sff8636_show_identifier(map); + switch (map->lower_memory[SFF8636_ID_OFFSET]) { + case SFF8024_ID_QSFP: + case SFF8024_ID_QSFP_PLUS: + case SFF8024_ID_QSFP28: + sff8636_show_page_zero(map); + sff8636_show_dom(map); + break; } } + +static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map, + const __u8 *id, __u32 eeprom_len) +{ + /* Lower Memory and Page 00h are always present. + * + * Offset into Upper Memory is between page size and twice the page + * size. Therefore, set the base address of each page to base address + * plus page size multiplied by the page number. + */ + map->lower_memory = id; + map->page_00h = id; + + /* Page 03h is only present when the module memory model is paged and + * not flat and when we got a big enough buffer from the kernel. + */ + if (map->lower_memory[SFF8636_STATUS_2_OFFSET] & + SFF8636_STATUS_PAGE_3_PRESENT || + eeprom_len != ETH_MODULE_SFF_8636_MAX_LEN) + return; + + map->page_03h = id + 3 * SFF8636_PAGE_SIZE; +} + +void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len) +{ + struct sff8636_memory_map map = {}; + + if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD || + id[SFF8636_ID_OFFSET] == SFF8024_ID_OSFP || + id[SFF8636_ID_OFFSET] == SFF8024_ID_DSFP) { + cmis_show_all_ioctl(id); + return; + } + + sff8636_memory_map_init_buf(&map, id, eeprom_len); + sff8636_show_all_common(&map); +} + +static void sff8636_request_init(struct ethtool_module_eeprom *request, u8 page, + u32 offset) +{ + request->offset = offset; + request->length = SFF8636_PAGE_SIZE; + request->page = page; + request->bank = 0; + request->i2c_address = SFF8636_I2C_ADDRESS; + request->data = NULL; +} + +static int +sff8636_memory_map_init_pages(struct cmd_context *ctx, + struct sff8636_memory_map *map) +{ + struct ethtool_module_eeprom request; + int ret; + + /* Lower Memory and Page 00h are always present. + * + * Offset into Upper Memory is between page size and twice the page + * size. Therefore, set the base address of each page to its base + * address minus page size. + */ + sff8636_request_init(&request, 0x0, 0); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->lower_memory = request.data; + + sff8636_request_init(&request, 0x0, SFF8636_PAGE_SIZE); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->page_00h = request.data - SFF8636_PAGE_SIZE; + + /* Page 03h is only present when the module memory model is paged and + * not flat. + */ + if (map->lower_memory[SFF8636_STATUS_2_OFFSET] & + SFF8636_STATUS_PAGE_3_PRESENT) + return 0; + + sff8636_request_init(&request, 0x3, SFF8636_PAGE_SIZE); + ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + return ret; + map->page_03h = request.data - SFF8636_PAGE_SIZE; + + return 0; +} + +int sff8636_show_all_nl(struct cmd_context *ctx) +{ + struct sff8636_memory_map map = {}; + int ret; + + ret = sff8636_memory_map_init_pages(ctx, &map); + if (ret < 0) + return ret; + sff8636_show_all_common(&map); + + return 0; +} @@ -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 */ @@ -63,6 +55,8 @@ #define SFF8636_TX2_FAULT_AW (1 << 1) #define SFF8636_TX1_FAULT_AW (1 << 0) +#define SFF8636_LOL_AW_OFFSET 0x05 + /* Module Monitor Interrupt Flags - 6-8 */ #define SFF8636_TEMP_AW_OFFSET 0x06 #define SFF8636_TEMP_HALARM_STATUS (1 << 7) @@ -188,7 +182,7 @@ #define SFF8636_PWR_MODE_OFFSET 0x5D #define SFF8636_HIGH_PWR_ENABLE (1 << 2) -#define SFF8636_LOW_PWR_MODE (1 << 1) +#define SFF8636_LOW_PWR_SET (1 << 1) #define SFF8636_PWR_OVERRIDE (1 << 0) #define SFF8636_TX_APP_SELECT_4_OFFSET 0x5E @@ -496,10 +490,52 @@ #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 +/* Tx input equalizers auto-adaptive */ +#define SFF8636_O2_TX_EQ_AUTO (1 << 3) /* Rx output amplitude */ #define SFF8636_O2_RX_OUTPUT_AMP (1 << 0) #define SFF8636_OPTION_3_OFFSET 0xC2 +/* Tx CDR Loss of Lock */ +#define SFF8636_O3_TX_LOL (1 << 5) +/* Rx CDR Loss of Lock */ +#define SFF8636_O3_RX_LOL (1 << 4) /* Rx Squelch Disable */ #define SFF8636_O3_RX_SQL_DSBL (1 << 3) /* Rx Output Disable capable */ @@ -564,32 +600,35 @@ * Offset - Page Num(3) * PageSize(0x80) + Page offset */ +/* 3 * 128 + Lower page 00h(128) */ +#define SFF8636_PAGE03H_OFFSET (128 * 4) + /* Module Thresholds (48 Bytes) 128-175 */ /* MSB at low address, LSB at high address */ -#define SFF8636_TEMP_HALRM 0x200 -#define SFF8636_TEMP_LALRM 0x202 -#define SFF8636_TEMP_HWARN 0x204 -#define SFF8636_TEMP_LWARN 0x206 - -#define SFF8636_VCC_HALRM 0x210 -#define SFF8636_VCC_LALRM 0x212 -#define SFF8636_VCC_HWARN 0x214 -#define SFF8636_VCC_LWARN 0x216 - -#define SFF8636_RX_PWR_HALRM 0x230 -#define SFF8636_RX_PWR_LALRM 0x232 -#define SFF8636_RX_PWR_HWARN 0x234 -#define SFF8636_RX_PWR_LWARN 0x236 - -#define SFF8636_TX_BIAS_HALRM 0x238 -#define SFF8636_TX_BIAS_LALRM 0x23A -#define SFF8636_TX_BIAS_HWARN 0x23C -#define SFF8636_TX_BIAS_LWARN 0x23E - -#define SFF8636_TX_PWR_HALRM 0x240 -#define SFF8636_TX_PWR_LALRM 0x242 -#define SFF8636_TX_PWR_HWARN 0x244 -#define SFF8636_TX_PWR_LWARN 0x246 +#define SFF8636_TEMP_HALRM 0x80 +#define SFF8636_TEMP_LALRM 0x82 +#define SFF8636_TEMP_HWARN 0x84 +#define SFF8636_TEMP_LWARN 0x86 + +#define SFF8636_VCC_HALRM 0x90 +#define SFF8636_VCC_LALRM 0x92 +#define SFF8636_VCC_HWARN 0x94 +#define SFF8636_VCC_LWARN 0x96 + +#define SFF8636_RX_PWR_HALRM 0xB0 +#define SFF8636_RX_PWR_LALRM 0xB2 +#define SFF8636_RX_PWR_HWARN 0xB4 +#define SFF8636_RX_PWR_LWARN 0xB6 + +#define SFF8636_TX_BIAS_HALRM 0xB8 +#define SFF8636_TX_BIAS_LALRM 0xBA +#define SFF8636_TX_BIAS_HWARN 0xBC +#define SFF8636_TX_BIAS_LWARN 0xBE + +#define SFF8636_TX_PWR_HALRM 0xC0 +#define SFF8636_TX_PWR_LALRM 0xC2 +#define SFF8636_TX_PWR_HWARN 0xC4 +#define SFF8636_TX_PWR_LWARN 0xC6 #define ETH_MODULE_SFF_8636_MAX_LEN 640 #define ETH_MODULE_SFF_8436_MAX_LEN 640 @@ -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; @@ -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); @@ -445,7 +446,7 @@ static int rmgr_find_empty_slot(struct rmgr_ctrl *rmgr, * If loc rolls over it should be greater than or equal to rmgr->size * and as such we know we have reached the end of the list. */ - if (!~(rmgr->slot[slot_num] | (~1UL << rmgr->size % BITS_PER_LONG))) { + if (!~(rmgr->slot[slot_num] | (~1UL << loc % BITS_PER_LONG))) { loc -= 1 + (loc % BITS_PER_LONG); slot_num--; } @@ -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)); @@ -596,7 +598,7 @@ int rxclass_rule_ins(struct cmd_context *ctx, else if (loc & RX_CLS_LOC_SPECIAL) printf("Added rule with ID %d\n", nfccmd.fs.location); - return 0; + return err; } int rxclass_rule_del(struct cmd_context *ctx, __u32 loc) @@ -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/scripts/ethtool-import-uapi b/scripts/ethtool-import-uapi new file mode 100755 index 0000000..a04a9c9 --- /dev/null +++ b/scripts/ethtool-import-uapi @@ -0,0 +1,67 @@ +#!/bin/bash -e +# +# ethtool-import-uapi [commit] +# +# Imports sanitized copies of kernel uapi headers from <commit> (can be +# a commit id, a tag or a branch name). If the argument is omitted, +# commit currently checked out in the kernel repository is used. + +sn="${0##*/}" +export ARCH="x86_64" +mkopt="-j$(nproc)" || mkopt='' + +if [ ! -d "$LINUX_GIT" ]; then + echo "${sn}: please set LINUX_GIT to the location of kernel git" >&2 + exit 1 +fi + +pushd "$LINUX_GIT" +if [ -n "$1" ]; then + git checkout "$1" +fi +desc=$(git describe --exact-match 2>/dev/null \ + || git show -s --abbrev=12 --pretty='commit %h') +kobj=$(mktemp -d) +make $mkopt O="$kobj" allmodconfig +make $mkopt O="$kobj" prepare +make $mkopt O="$kobj" INSTALL_HDR_PATH="${kobj}/hdr" headers_install +popd + +pushd uapi +find . -type f -name '*.h' -exec cp -v "${kobj}/hdr/include/{}" {} \; + +go_on=true +while $go_on; do + go_on=false + while read f; do + if [ "${f#asm/}" != "$f" ]; then + # skip architecture dependent asm/ headers + continue + fi + if [ -f "$f" ]; then + # already present + continue + fi + if [ ! -f "${kobj}/hdr/include/${f}" ]; then + # not a kernel header + continue + fi + echo "+ add $f" + go_on=true + mkdir -p "${f%/*}" + cp "${kobj}/hdr/include/${f}" "${f}" + done < <( + find . -type f -name '*.[ch]' -exec sed -nre '\_^[[:blank:]]*#include[[:blank:]]<.+>_ { s_^[[:blank:]]*#include[[:blank:]]<([^>]*)>.*$_\1_ ; p }' {} \; \ + | LC_ALL=C sort -u + ) +done +popd +rm -rf "$kobj" + +git add uapi +git commit -s -F - <<EOT +update UAPI header copies + +Update to kernel ${desc}. + +EOT @@ -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..a5c1510 100644 --- a/sff-common.c +++ b/sff-common.c @@ -53,6 +53,23 @@ void sff_show_ascii(const __u8 *id, unsigned int first_reg, printf("\n"); } +void sff_show_lane_status(const char *name, unsigned int lane_cnt, + const char *yes, const char *no, unsigned int value) +{ + printf("\t%-41s : ", name); + if (!value) { + printf("None\n"); + return; + } + + printf("["); + while (lane_cnt--) { + printf(" %s%c", value & 1 ? yes : no, lane_cnt ? ',': ' '); + value >>= 1; + } + printf("]\n"); +} + void sff8024_show_oui(const __u8 *id, int id_offset) { printf("\t%-41s : %02x:%02x:%02x\n", "Vendor OUI", @@ -136,6 +153,15 @@ 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; + case SFF8024_ID_OSFP: + printf(" (OSFP 8X Pluggable Transceiver)\n"); + break; + case SFF8024_ID_DSFP: + printf(" (DSFP Dual Small Form Factor Pluggable Transceiver)\n"); + break; default: printf(" (reserved or unknown)\n"); break; @@ -203,6 +229,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 +340,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..57bcc4a 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,10 @@ #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_OSFP 0x19 +#define SFF8024_ID_DSFP 0x1B +#define SFF8024_ID_LAST SFF8024_ID_DSFP #define SFF8024_ID_UNALLOCATED_LAST 0x7F #define SFF8024_ID_VENDOR_START 0x80 #define SFF8024_ID_VENDOR_LAST 0xFF @@ -76,8 +89,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 @@ -108,8 +127,8 @@ #define SFF8024_ENCODING_PAM4 0x08 /* Most common case: 16-bit unsigned integer in a certain unit */ -#define OFFSET_TO_U16(offset) \ - (id[offset] << 8 | id[(offset) + 1]) +#define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1]) +#define OFFSET_TO_U16(offset) OFFSET_TO_U16_PTR(id, offset) # define PRINT_xX_PWR(string, var) \ printf("\t%-41s : %.4f mW / %.2f dBm\n", (string), \ @@ -142,7 +161,7 @@ struct sff_channel_diags { /* Module Monitoring Fields */ struct sff_diags { -#define MAX_CHANNEL_NUM 4 +#define MAX_CHANNEL_NUM 32 #define LWARN 0 #define HWARN 1 #define LALRM 2 @@ -179,11 +198,14 @@ void sff_show_value_with_unit(const __u8 *id, unsigned int reg, const char *unit); void sff_show_ascii(const __u8 *id, unsigned int first_reg, unsigned int last_reg, const char *name); +void sff_show_lane_status(const char *name, unsigned int lane_cnt, + const char *yes, const char *no, unsigned int value); void sff_show_thresholds(struct sff_diags sd); 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__ */ @@ -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) { @@ -8,8 +8,14 @@ */ #include <stdio.h> +#include <errno.h> #include "internal.h" #include "sff-common.h" +#include "netlink/extapi.h" + +#define SFF8079_PAGE_SIZE 0x80 +#define SFF8079_I2C_ADDRESS_LOW 0x50 +#define SFF8079_I2C_ADDRESS_HIGH 0x51 static void sff8079_show_identifier(const __u8 *id) { @@ -47,7 +53,7 @@ static void sff8079_show_transceiver(const __u8 *id) /* 10G Ethernet Compliance Codes */ if (id[3] & (1 << 7)) printf("%s 10G Ethernet: 10G Base-ER" \ - " [SFF-8472 rev10.4 only]\n", pfx); + " [SFF-8472 rev10.4 onwards]\n", pfx); if (id[3] & (1 << 6)) printf("%s 10G Ethernet: 10G Base-LRM\n", pfx); if (id[3] & (1 << 5)) @@ -185,10 +191,82 @@ static void sff8079_show_transceiver(const __u8 *id) printf("%s Extended: 25G Base-CR CA-S\n", pfx); if (id[36] == 0xd) printf("%s Extended: 25G Base-CR CA-N\n", pfx); + if (id[36] == 0x16) + printf("%s Extended: 10Gbase-T with SFI electrical interface\n", pfx); if (id[36] == 0x18) 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) @@ -324,15 +402,28 @@ static void sff8079_show_options(const __u8 *id) printf("%s Power level 3 requirement\n", pfx); } -void sff8079_show_all(const __u8 *id) +static void sff8079_show_all_common(const __u8 *id) { sff8079_show_identifier(id); if (((id[0] == 0x02) || (id[0] == 0x03)) && (id[1] == 0x04)) { + unsigned int br_nom, br_min, br_max; + + if (id[12] == 0) { + br_nom = br_min = br_max = 0; + } else if (id[12] == 255) { + br_nom = id[66] * 250; + br_max = id[67]; + br_min = id[67]; + } else { + br_nom = id[12] * 100; + br_max = id[66]; + br_min = id[67]; + } sff8079_show_ext_identifier(id); sff8079_show_connector(id); sff8079_show_transceiver(id); sff8079_show_encoding(id); - sff8079_show_value_with_unit(id, 12, "BR, Nominal", 100, "MBd"); + printf("\t%-41s : %u%s\n", "BR, Nominal", br_nom, "MBd"); sff8079_show_rate_identifier(id); sff8079_show_value_with_unit(id, 14, "Length (SMF,km)", 1, "km"); @@ -348,9 +439,67 @@ void sff8079_show_all(const __u8 *id) sff8079_show_ascii(id, 40, 55, "Vendor PN"); sff8079_show_ascii(id, 56, 59, "Vendor rev"); sff8079_show_options(id); - sff8079_show_value_with_unit(id, 66, "BR margin, max", 1, "%"); - sff8079_show_value_with_unit(id, 67, "BR margin, min", 1, "%"); + printf("\t%-41s : %u%s\n", "BR margin, max", br_max, "%"); + printf("\t%-41s : %u%s\n", "BR margin, min", br_min, "%"); sff8079_show_ascii(id, 68, 83, "Vendor SN"); sff8079_show_ascii(id, 84, 91, "Date code"); } } + +void sff8079_show_all_ioctl(const __u8 *id) +{ + sff8079_show_all_common(id); +} + +static int sff8079_get_eeprom_page(struct cmd_context *ctx, u8 i2c_address, + __u8 *buf) +{ + struct ethtool_module_eeprom request = { + .length = SFF8079_PAGE_SIZE, + .i2c_address = i2c_address, + }; + int ret; + + ret = nl_get_eeprom_page(ctx, &request); + if (!ret) + memcpy(buf, request.data, SFF8079_PAGE_SIZE); + + return ret; +} + +int sff8079_show_all_nl(struct cmd_context *ctx) +{ + u8 *buf; + int ret; + + /* The SFF-8472 parser expects a single buffer that contains the + * concatenation of the first 256 bytes from addresses A0h and A2h, + * respectively. + */ + buf = calloc(1, ETH_MODULE_SFF_8472_LEN); + if (!buf) + return -ENOMEM; + + /* Read A0h page */ + ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_LOW, buf); + if (ret) + goto out; + + sff8079_show_all_common(buf); + + /* Finish if A2h page is not present */ + if (!(buf[92] & (1 << 6))) + goto out; + + /* Read A2h page */ + ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_HIGH, + buf + ETH_MODULE_SFF_8079_LEN); + if (ret) + goto out; + + sff8472_show_all(buf); +out: + free(buf); + + return ret; +} diff --git a/shell-completion/bash/ethtool b/shell-completion/bash/ethtool index 5305559..99c5f6f 100644 --- a/shell-completion/bash/ethtool +++ b/shell-completion/bash/ethtool @@ -97,6 +97,7 @@ _ethtool_change() [speed]=notseen [wol]=notseen [xcvr]=notseen + [lanes]=notseen ) local -A msgtypes=( @@ -175,6 +176,9 @@ _ethtool_change() xcvr) COMPREPLY=( $( compgen -W 'internal external' -- "$cur" ) ) return ;; + lanes) + # Number + return ;; esac local -a comp_words=() @@ -250,6 +254,9 @@ _ethtool_coalesce() [tx-usecs-high]=1 [tx-usecs-irq]=1 [tx-usecs-low]=1 + [tx-aggr-max-bytes]=1 + [tx-aggr-max-frames]=1 + [tx-aggr-time-usecs]=1 ) case "$prev" in @@ -1133,6 +1140,27 @@ _ethtool_test() fi } +# Completion for ethtool --set-module +_ethtool_set_module() +{ + local -A settings=( + [power-mode-policy]=1 + ) + + case "$prev" in + power-mode-policy) + COMPREPLY=( $( compgen -W 'high auto' -- "$cur" ) ) + return ;; + esac + + # Remove settings which have been seen + local word + for word in "${words[@]:3:${#words[@]}-4}"; do + unset "settings[$word]" + done + + COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) ) +} # Complete any ethtool command _ethtool() @@ -1185,6 +1213,8 @@ _ethtool() [--show-time-stamping]=devname [--statistics]=devname [--test]=test + [--set-module]=set_module + [--show-module]=devname ) local -A other_funcs=( [--config-ntuple]=config_nfc @@ -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; @@ -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/test-cmdline.c b/test-cmdline.c index b76e2c3..cb803ed 100644 --- a/test-cmdline.c +++ b/test-cmdline.c @@ -12,6 +12,12 @@ #define TEST_NO_WRAPPERS #include "internal.h" +#ifdef ETHTOOL_ENABLE_NETLINK +#define IS_NL 1 +#else +#define IS_NL 0 +#endif + static struct test_case { int rc; const char *args; @@ -19,7 +25,10 @@ static struct test_case { { 1, "" }, { 0, "devname" }, { 0, "15_char_devname" }, - { 1, "16_char_devname!" }, + /* netlink interface allows names up to 127 characters */ + { !IS_NL, "16_char_devname!" }, + { !IS_NL, "127_char_devname0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde" }, + { 1, "128_char_devname0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" }, /* Argument parsing for -s is specialised */ { 0, "-s devname" }, { 0, "--change devname speed 100 duplex half mdix auto" }, @@ -55,7 +64,8 @@ static struct test_case { { 0, "-s devname phyad 1" }, { 1, "--change devname phyad foo" }, { 1, "-s devname phyad" }, - { 0, "--change devname xcvr external" }, + /* Deprecated 'xcvr' detected by netlink parser */ + { IS_NL, "--change devname xcvr external" }, { 1, "-s devname xcvr foo" }, { 1, "--change devname xcvr" }, { 0, "-s devname wol p" }, @@ -69,7 +79,9 @@ static struct test_case { { 0, "-s devname msglvl hw on rx_status off" }, { 1, "--change devname msglvl hw foo" }, { 1, "-s devname msglvl hw" }, - { 0, "--change devname speed 100 duplex half port tp autoneg on advertise 0x1 phyad 1 xcvr external wol p sopass 01:23:45:67:89:ab msglvl 1" }, + { 0, "--change devname speed 100 duplex half port tp autoneg on advertise 0x1 phyad 1 wol p sopass 01:23:45:67:89:ab msglvl 1" }, + /* Deprecated 'xcvr' detected by netlink parser */ + { IS_NL, "--change devname speed 100 duplex half port tp autoneg on advertise 0x1 phyad 1 xcvr external wol p sopass 01:23:45:67:89:ab msglvl 1" }, { 1, "-s devname foo" }, { 1, "-s" }, { 0, "-a devname" }, @@ -275,9 +287,10 @@ static struct test_case { { 0, "--set-fec devname encoding off" }, { 0, "--set-fec devname encoding baser rs" }, { 0, "--set-fec devname encoding auto auto" }, - { 1, "--set-fec devname encoding foo" }, - { 1, "--set-fec devname encoding auto foo" }, - { 1, "--set-fec devname encoding none" }, + /* encoding names are validated by kernel with netlink */ + { !IS_NL, "--set-fec devname encoding foo" }, + { !IS_NL, "--set-fec devname encoding auto foo" }, + { !IS_NL, "--set-fec devname encoding none" }, { 1, "--set-fec devname auto" }, /* can't test --set-priv-flags yet */ { 0, "-h" }, @@ -288,12 +301,24 @@ static struct test_case { { 1, "-0" }, }; -int send_ioctl(struct cmd_context *ctx, void *cmd) +int send_ioctl(struct cmd_context *ctx __maybe_unused, void *cmd __maybe_unused) { /* If we get this far then parsing succeeded */ test_exit(0); } +#ifdef ETHTOOL_ENABLE_NETLINK +struct nl_socket; +struct nl_msg_buff; + +ssize_t nlsock_sendmsg(struct nl_socket *nlsk __maybe_unused, + struct nl_msg_buff *altbuff __maybe_unused) +{ + /* If we get this far then parsing succeeded */ + test_exit(0); +} +#endif + int main(void) { struct test_case *tc; diff --git a/test-common.c b/test-common.c index cd63d1d..1dab0ce 100644 --- a/test-common.c +++ b/test-common.c @@ -293,12 +293,14 @@ int test_ioctl(const struct cmd_expect *expect, void *cmd) int test_cmdline(const char *args) { - int argc, i; - char **argv; + int volatile orig_stdout_fd = -1; + int volatile orig_stderr_fd = -1; + char **volatile argv; + int volatile argc; + int dev_null = -1; const char *arg; size_t len; - int dev_null = -1, orig_stdout_fd = -1, orig_stderr_fd = -1; - int rc; + int rc, i; /* Convert line to argv */ argc = 1; diff --git a/test-features.c b/test-features.c index 6ebb364..b9f80f0 100644 --- a/test-features.c +++ b/test-features.c @@ -511,6 +511,17 @@ int send_ioctl(struct cmd_context *ctx, void *cmd) return rc; } +#ifdef ETHTOOL_ENABLE_NETLINK +struct nl_socket; +struct nl_msg_buff; + +ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *altbuff) +{ + /* Should not be called with test-features */ + exit(1); +} +#endif + int main(void) { const struct test_case *tc; @@ -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"); @@ -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/const.h b/uapi/linux/const.h new file mode 100644 index 0000000..1eb84b5 --- /dev/null +++ b/uapi/linux/const.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* const.h: Macros for dealing with constants. */ + +#ifndef _LINUX_CONST_H +#define _LINUX_CONST_H + +/* Some constant macros are used in both assembler and + * C code. Therefore we cannot annotate them always with + * 'UL' and other type specifiers unilaterally. We + * use the following macros to deal with this. + * + * Similarly, _AT() will cast an expression with a type in C, but + * leave it unchanged in asm. + */ + +#ifdef __ASSEMBLY__ +#define _AC(X,Y) X +#define _AT(T,X) X +#else +#define __AC(X,Y) (X##Y) +#define _AC(X,Y) __AC(X,Y) +#define _AT(T,X) ((T)(X)) +#endif + +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + +#define _BITUL(x) (_UL(1) << (x)) +#define _BITULL(x) (_ULL(1) << (x)) + +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) + +#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#endif /* _LINUX_CONST_H */ diff --git a/ethtool-copy.h b/uapi/linux/ethtool.h index 9afd2e6..1d0731b 100644 --- a/ethtool-copy.h +++ b/uapi/linux/ethtool.h @@ -14,7 +14,7 @@ #ifndef _LINUX_ETHTOOL_H #define _LINUX_ETHTOOL_H -#include <linux/kernel.h> +#include <linux/const.h> #include <linux/types.h> #include <linux/if_ether.h> @@ -24,6 +24,14 @@ * have the same layout for 32-bit and 64-bit userland. */ +/* Note on reserved space. + * Reserved fields must not be accessed directly by user space because + * they may be replaced by a different field in the future. They must + * be initialized to zero before making the request, e.g. via memset + * of the entire structure or implicitly by not being set in a structure + * initializer. + */ + /** * struct ethtool_cmd - DEPRECATED, link control and status * This structure is DEPRECATED, please use struct ethtool_link_settings. @@ -65,6 +73,7 @@ * and other link features that the link partner advertised * through autonegotiation; 0 if unknown or not applicable. * Read-only. + * @reserved: Reserved for future use; see the note on reserved space. * * The link speed in Mbps is split between @speed and @speed_hi. Use * the ethtool_cmd_speed() and ethtool_cmd_speed_set() functions to @@ -148,11 +157,14 @@ static __inline__ __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) * in its bus driver structure (e.g. pci_driver::name). Must * not be an empty string. * @version: Driver version string; may be an empty string - * @fw_version: Firmware version string; may be an empty string - * @erom_version: Expansion ROM version string; may be an empty string + * @fw_version: Firmware version string; driver defined; may be an + * empty string + * @erom_version: Expansion ROM version string; driver defined; may be + * an empty string * @bus_info: Device bus address. This should match the dev_name() * string for the underlying bus device, if there is one. May be * an empty string. + * @reserved2: Reserved for future use; see the note on reserved space. * @n_priv_flags: Number of flags valid for %ETHTOOL_GPFLAGS and * %ETHTOOL_SPFLAGS commands; also the number of strings in the * %ETH_SS_PRIV_FLAGS set @@ -167,10 +179,6 @@ static __inline__ __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) * * Users can use the %ETHTOOL_GSSET_INFO command to get the number of * strings in any string set (from Linux 2.6.34). - * - * Drivers should set at most @driver, @version, @fw_version and - * @bus_info in their get_drvinfo() implementation. The ethtool - * core fills in the other fields using other driver operations. */ struct ethtool_drvinfo { __u32 cmd; @@ -219,9 +227,10 @@ enum tunable_id { ETHTOOL_RX_COPYBREAK, ETHTOOL_TX_COPYBREAK, ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ + ETHTOOL_TX_COPYBREAK_BUF_SIZE, /* * Add your fresh new tunable attribute above and remember to update - * tunable_strings[] in net/core/ethtool.c + * tunable_strings[] in net/ethtool/common.c */ __ETHTOOL_TUNABLE_COUNT, }; @@ -244,7 +253,7 @@ struct ethtool_tunable { __u32 id; __u32 type_id; __u32 len; - void *data[0]; + void *data[]; }; #define DOWNSHIFT_DEV_DEFAULT_COUNT 0xff @@ -285,7 +294,7 @@ enum phy_tunable_id { ETHTOOL_PHY_EDPD, /* * Add your fresh new phy tunable attribute above and remember to update - * phy_tunable_strings[] in net/core/ethtool.c + * phy_tunable_strings[] in net/ethtool/common.c */ __ETHTOOL_PHY_TUNABLE_COUNT, }; @@ -309,7 +318,7 @@ struct ethtool_regs { __u32 cmd; __u32 version; __u32 len; - __u8 data[0]; + __u8 data[]; }; /** @@ -335,7 +344,7 @@ struct ethtool_eeprom { __u32 magic; __u32 offset; __u32 len; - __u8 data[0]; + __u8 data[]; }; /** @@ -354,6 +363,7 @@ struct ethtool_eeprom { * @tx_lpi_timer: Time in microseconds the interface delays prior to asserting * its tx lpi (after reaching 'idle' state). Effective only when eee * was negotiated and tx_lpi_enabled was set. + * @reserved: Reserved for future use; see the note on reserved space. */ struct ethtool_eee { __u32 cmd; @@ -372,6 +382,7 @@ struct ethtool_eee { * @cmd: %ETHTOOL_GMODULEINFO * @type: Standard the module information conforms to %ETH_MODULE_SFF_xxxx * @eeprom_len: Length of the eeprom + * @reserved: Reserved for future use; see the note on reserved space. * * This structure is used to return the information to * properly size memory for a subsequent call to %ETHTOOL_GMODULEEEPROM. @@ -577,6 +588,70 @@ struct ethtool_pauseparam { __u32 tx_pause; }; +/* 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, + ETHTOOL_LINK_EXT_STATE_MODULE, +}; + +/* 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, +}; + +/* 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, +}; + +/* 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, +}; + +/* 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, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS, +}; + +/* 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, +}; + +/* More information in addition to ETHTOOL_LINK_EXT_STATE_MODULE. */ +enum ethtool_link_ext_substate_module { + ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY = 1, +}; + #define ETH_GSTRING_LEN 32 /** @@ -589,8 +664,23 @@ struct ethtool_pauseparam { * now deprecated * @ETH_SS_FEATURES: Device feature names * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names + * @ETH_SS_TUNABLES: tunable names * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS * @ETH_SS_PHY_TUNABLES: PHY tunable names + * @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 + * @ETH_SS_STATS_STD: standardized stats + * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics + * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics + * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics + * @ETH_SS_STATS_RMON: names of RMON statistics + * + * @ETH_SS_COUNT: number of defined string sets */ enum ethtool_stringset { ETH_SS_TEST = 0, @@ -602,6 +692,132 @@ enum ethtool_stringset { ETH_SS_TUNABLES, ETH_SS_PHY_STATS, ETH_SS_PHY_TUNABLES, + 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, + ETH_SS_STATS_STD, + ETH_SS_STATS_ETH_PHY, + ETH_SS_STATS_ETH_MAC, + ETH_SS_STATS_ETH_CTRL, + ETH_SS_STATS_RMON, + + /* add new constants above here */ + ETH_SS_COUNT +}; + +/** + * enum ethtool_mac_stats_src - source of ethtool MAC statistics + * @ETHTOOL_MAC_STATS_SRC_AGGREGATE: + * if device supports a MAC merge layer, this retrieves the aggregate + * statistics of the eMAC and pMAC. Otherwise, it retrieves just the + * statistics of the single (express) MAC. + * @ETHTOOL_MAC_STATS_SRC_EMAC: + * if device supports a MM layer, this retrieves the eMAC statistics. + * Otherwise, it retrieves the statistics of the single (express) MAC. + * @ETHTOOL_MAC_STATS_SRC_PMAC: + * if device supports a MM layer, this retrieves the pMAC statistics. + */ +enum ethtool_mac_stats_src { + ETHTOOL_MAC_STATS_SRC_AGGREGATE, + ETHTOOL_MAC_STATS_SRC_EMAC, + ETHTOOL_MAC_STATS_SRC_PMAC, +}; + +/** + * enum ethtool_module_power_mode_policy - plug-in module power mode policy + * @ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: Module is always in high power mode. + * @ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO: Module is transitioned by the host + * to high power mode when the first port using it is put administratively + * up and to low power mode when the last port using it is put + * administratively down. + */ +enum ethtool_module_power_mode_policy { + ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH = 1, + ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO, +}; + +/** + * enum ethtool_module_power_mode - plug-in module power mode + * @ETHTOOL_MODULE_POWER_MODE_LOW: Module is in low power mode. + * @ETHTOOL_MODULE_POWER_MODE_HIGH: Module is in high power mode. + */ +enum ethtool_module_power_mode { + ETHTOOL_MODULE_POWER_MODE_LOW = 1, + ETHTOOL_MODULE_POWER_MODE_HIGH, +}; + +/** + * enum ethtool_podl_pse_admin_state - operational state of the PoDL PSE + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState + * @ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN: state of PoDL PSE functions are + * unknown + * @ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: PoDL PSE functions are disabled + * @ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: PoDL PSE functions are enabled + */ +enum ethtool_podl_pse_admin_state { + ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN = 1, + ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED, + ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED, +}; + +/** + * enum ethtool_podl_pse_pw_d_status - power detection status of the PoDL PSE. + * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus: + * @ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN: PoDL PSE + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED: "The enumeration “disabled” is + * asserted true when the PoDL PSE state diagram variable mr_pse_enable is + * false" + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING: "The enumeration “searching” is + * asserted true when either of the PSE state diagram variables + * pi_detecting or pi_classifying is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING: "The enumeration “deliveringPower” + * is asserted true when the PoDL PSE state diagram variable pi_powered is + * true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP: "The enumeration “sleep” is asserted + * true when the PoDL PSE state diagram variable pi_sleeping is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE: "The enumeration “idle” is asserted true + * when the logical combination of the PoDL PSE state diagram variables + * pi_prebiased*!pi_sleeping is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR: "The enumeration “error” is asserted + * true when the PoDL PSE state diagram variable overload_held is true." + */ +enum ethtool_podl_pse_pw_d_status { + ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN = 1, + ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED, + ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING, + ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING, + ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP, + ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE, + ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR, +}; + +/** + * enum ethtool_mm_verify_status - status of MAC Merge Verify function + * @ETHTOOL_MM_VERIFY_STATUS_UNKNOWN: + * verification status is unknown + * @ETHTOOL_MM_VERIFY_STATUS_INITIAL: + * the 802.3 Verify State diagram is in the state INIT_VERIFICATION + * @ETHTOOL_MM_VERIFY_STATUS_VERIFYING: + * the Verify State diagram is in the state VERIFICATION_IDLE, + * SEND_VERIFY or WAIT_FOR_RESPONSE + * @ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED: + * indicates that the Verify State diagram is in the state VERIFIED + * @ETHTOOL_MM_VERIFY_STATUS_FAILED: + * the Verify State diagram is in the state VERIFY_FAIL + * @ETHTOOL_MM_VERIFY_STATUS_DISABLED: + * verification of preemption operation is disabled + */ +enum ethtool_mm_verify_status { + ETHTOOL_MM_VERIFY_STATUS_UNKNOWN, + ETHTOOL_MM_VERIFY_STATUS_INITIAL, + ETHTOOL_MM_VERIFY_STATUS_VERIFYING, + ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED, + ETHTOOL_MM_VERIFY_STATUS_FAILED, + ETHTOOL_MM_VERIFY_STATUS_DISABLED, }; /** @@ -620,12 +836,13 @@ struct ethtool_gstrings { __u32 cmd; __u32 string_set; __u32 len; - __u8 data[0]; + __u8 data[]; }; /** * struct ethtool_sset_info - string set information * @cmd: Command number = %ETHTOOL_GSSET_INFO + * @reserved: Reserved for future use; see the note on reserved space. * @sset_mask: On entry, a bitmask of string sets to query, with bits * numbered according to &enum ethtool_stringset. On return, a * bitmask of those string sets queried that are supported. @@ -644,7 +861,7 @@ struct ethtool_sset_info { __u32 cmd; __u32 reserved; __u64 sset_mask; - __u32 data[0]; + __u32 data[]; }; /** @@ -670,6 +887,7 @@ enum ethtool_test_flags { * @flags: A bitmask of flags from &enum ethtool_test_flags. Some * flags may be set by the user on entry; others may be set by * the driver on return. + * @reserved: Reserved for future use; see the note on reserved space. * @len: On return, the number of test results * @data: Array of test results * @@ -683,7 +901,7 @@ struct ethtool_test { __u32 flags; __u32 reserved; __u32 len; - __u64 data[0]; + __u64 data[]; }; /** @@ -700,7 +918,7 @@ struct ethtool_test { struct ethtool_stats { __u32 cmd; __u32 n_stats; - __u64 data[0]; + __u64 data[]; }; /** @@ -717,7 +935,7 @@ struct ethtool_stats { struct ethtool_perm_addr { __u32 cmd; __u32 size; - __u8 data[0]; + __u8 data[]; }; /* boolean flags controlling per-interface behavior characteristics. @@ -870,6 +1088,7 @@ union ethtool_flow_union { * @vlan_etype: VLAN EtherType * @vlan_tci: VLAN tag control information * @data: user defined data + * @padding: Reserved for future use; see the note on reserved space. * * Note, @vlan_etype, @vlan_tci, and @data are only valid if %FLOW_EXT * is set in &struct ethtool_rx_flow_spec @flow_type. @@ -1005,7 +1224,7 @@ struct ethtool_rxnfc { __u32 rule_cnt; __u32 rss_context; }; - __u32 rule_locs[0]; + __u32 rule_locs[]; }; @@ -1025,7 +1244,7 @@ struct ethtool_rxnfc { struct ethtool_rxfh_indir { __u32 cmd; __u32 size; - __u32 ring_index[0]; + __u32 ring_index[]; }; /** @@ -1045,7 +1264,8 @@ struct ethtool_rxfh_indir { * hardware hash key. * @hfunc: Defines the current RSS hash function used by HW (or to be set to). * Valid values are one of the %ETH_RSS_HASH_*. - * @rsvd: Reserved for future extensions. + * @rsvd8: Reserved for future use; see the note on reserved space. + * @rsvd32: Reserved for future use; see the note on reserved space. * @rss_config: RX ring/queue index for each hash value i.e., indirection table * of @indir_size __u32 elements, followed by hash key of @key_size * bytes. @@ -1065,7 +1285,7 @@ struct ethtool_rxfh { __u8 hfunc; __u8 rsvd8[3]; __u32 rsvd32; - __u32 rss_config[0]; + __u32 rss_config[]; }; #define ETH_RXFH_CONTEXT_ALLOC 0xffffffff #define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff @@ -1150,7 +1370,7 @@ struct ethtool_dump { __u32 version; __u32 flag; __u32 len; - __u8 data[0]; + __u8 data[]; }; #define ETH_FW_DUMP_DISABLE 0 @@ -1182,7 +1402,7 @@ struct ethtool_get_features_block { struct ethtool_gfeatures { __u32 cmd; __u32 size; - struct ethtool_get_features_block features[0]; + struct ethtool_get_features_block features[]; }; /** @@ -1204,7 +1424,7 @@ struct ethtool_set_features_block { struct ethtool_sfeatures { __u32 cmd; __u32 size; - struct ethtool_set_features_block features[0]; + struct ethtool_set_features_block features[]; }; /** @@ -1213,7 +1433,9 @@ struct ethtool_sfeatures { * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags * @phc_index: device index of the associated PHC, or -1 if there is none * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values + * @tx_reserved: Reserved for future use; see the note on reserved space. * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values + * @rx_reserved: Reserved for future use; see the note on reserved space. * * The bits in the 'tx_types' and 'rx_filters' fields correspond to * the 'hwtstamp_tx_types' and 'hwtstamp_rx_filters' enumeration values, @@ -1287,15 +1509,33 @@ struct ethtool_per_queue_op { }; /** - * struct ethtool_fecparam - Ethernet forward error correction(fec) parameters + * struct ethtool_fecparam - Ethernet Forward Error Correction parameters * @cmd: Command number = %ETHTOOL_GFECPARAM or %ETHTOOL_SFECPARAM - * @active_fec: FEC mode which is active on porte - * @fec: Bitmask of supported/configured FEC modes - * @rsvd: Reserved for future extensions. i.e FEC bypass feature. + * @active_fec: FEC mode which is active on the port, single bit set, GET only. + * @fec: Bitmask of configured FEC modes. + * @reserved: Reserved for future extensions, ignore on GET, write 0 for SET. * - * Drivers should reject a non-zero setting of @autoneg when - * autoneogotiation is disabled (or not supported) for the link. + * Note that @reserved was never validated on input and ethtool user space + * left it uninitialized when calling SET. Hence going forward it can only be + * used to return a value to userspace with GET. * + * FEC modes supported by the device can be read via %ETHTOOL_GLINKSETTINGS. + * FEC settings are configured by link autonegotiation whenever it's enabled. + * With autoneg on %ETHTOOL_GFECPARAM can be used to read the current mode. + * + * When autoneg is disabled %ETHTOOL_SFECPARAM controls the FEC settings. + * It is recommended that drivers only accept a single bit set in @fec. + * When multiple bits are set in @fec drivers may pick mode in an implementation + * dependent way. Drivers should reject mixing %ETHTOOL_FEC_AUTO_BIT with other + * FEC modes, because it's unclear whether in this case other modes constrain + * AUTO or are independent choices. + * Drivers must reject SET requests if they support none of the requested modes. + * + * If device does not support FEC drivers may use %ETHTOOL_FEC_NONE instead + * of returning %EOPNOTSUPP from %ETHTOOL_GFECPARAM. + * + * See enum ethtool_fec_config_bits for definition of valid bits for both + * @fec and @active_fec. */ struct ethtool_fecparam { __u32 cmd; @@ -1307,11 +1547,16 @@ struct ethtool_fecparam { /** * enum ethtool_fec_config_bits - flags definition of ethtool_fec_configuration - * @ETHTOOL_FEC_NONE: FEC mode configuration is not supported - * @ETHTOOL_FEC_AUTO: Default/Best FEC mode provided by driver - * @ETHTOOL_FEC_OFF: No FEC Mode - * @ETHTOOL_FEC_RS: Reed-Solomon Forward Error Detection mode - * @ETHTOOL_FEC_BASER: Base-R/Reed-Solomon Forward Error Detection mode + * @ETHTOOL_FEC_NONE_BIT: FEC mode configuration is not supported. Should not + * be used together with other bits. GET only. + * @ETHTOOL_FEC_AUTO_BIT: Select default/best FEC mode automatically, usually + * based link mode and SFP parameters read from module's + * EEPROM. This bit does _not_ mean autonegotiation. + * @ETHTOOL_FEC_OFF_BIT: No FEC Mode + * @ETHTOOL_FEC_RS_BIT: Reed-Solomon FEC Mode + * @ETHTOOL_FEC_BASER_BIT: Base-R/Reed-Solomon FEC Mode + * @ETHTOOL_FEC_LLRS_BIT: Low Latency Reed Solomon FEC Mode (25G/50G Ethernet + * Consortium) */ enum ethtool_fec_config_bits { ETHTOOL_FEC_NONE_BIT, @@ -1319,6 +1564,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) @@ -1326,6 +1572,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. @@ -1505,6 +1752,39 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66, ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68, + ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT = 69, + ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT = 70, + 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, + ETHTOOL_LINK_MODE_10baseT1L_Full_BIT = 92, + ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT = 93, + ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT = 94, + ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT = 95, + ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT = 96, + ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT = 97, + ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT = 98, + ETHTOOL_LINK_MODE_10baseT1S_Full_BIT = 99, + ETHTOOL_LINK_MODE_10baseT1S_Half_BIT = 100, + ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT = 101, /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS @@ -1616,6 +1896,8 @@ enum ethtool_link_mode_bit_indices { #define SPEED_56000 56000 #define SPEED_100000 100000 #define SPEED_200000 200000 +#define SPEED_400000 400000 +#define SPEED_800000 800000 #define SPEED_UNKNOWN -1 @@ -1641,6 +1923,32 @@ 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 + +/* These are used to throttle the rate of data on the phy interface when the + * native speed of the interface is higher than the link speed. These should + * not be used for phy interfaces which natively support multiple speeds (e.g. + * MII or SGMII). + */ +/* No rate matching performed. */ +#define RATE_MATCH_NONE 0 +/* The phy sends pause frames to throttle the MAC. */ +#define RATE_MATCH_PAUSE 1 +/* The phy asserts CRS to prevent the MAC from transmitting. */ +#define RATE_MATCH_CRS 2 +/* The MAC is programmed with a sufficiently-large IPG. */ +#define RATE_MATCH_OPEN_LOOP 3 + /* Which connector port. */ #define PORT_TP 0x00 #define PORT_AUI 0x01 @@ -1680,6 +1988,8 @@ static __inline__ int ethtool_validate_duplex(__u8 duplex) #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ #define WAKE_FILTER (1 << 7) +#define WOL_MODE_COUNT 8 + /* L2-L4 network traffic flow types */ #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ #define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ @@ -1830,6 +2140,11 @@ enum ethtool_reset_flags { * autonegotiation; 0 if unknown or not applicable. Read-only. * @transceiver: Used to distinguish different possible PHY types, * reported consistently by PHYLIB. Read-only. + * @master_slave_cfg: Master/slave port mode. + * @master_slave_state: Master/slave port state. + * @rate_matching: Rate adaptation performed by the PHY + * @reserved: Reserved for future use; see the note on reserved space. + * @link_mode_masks: Variable length bitmaps. * * If autonegotiation is disabled, the speed and @duplex represent the * fixed link mode and are writable if the driver supports multiple @@ -1877,9 +2192,11 @@ 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 rate_matching; __u32 reserved[7]; - __u32 link_mode_masks[0]; + __u32 link_mode_masks[]; /* layout of link_mode_masks fields: * __u32 map_supported[link_mode_masks_nwords]; * __u32 map_advertising[link_mode_masks_nwords]; diff --git a/uapi/linux/ethtool_netlink.h b/uapi/linux/ethtool_netlink.h new file mode 100644 index 0000000..a8b0d79 --- /dev/null +++ b/uapi/linux/ethtool_netlink.h @@ -0,0 +1,984 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool + * + * See Documentation/networking/ethtool-netlink.rst in kernel source tree for + * doucumentation of the interface. + */ + +#ifndef _LINUX_ETHTOOL_NETLINK_H_ +#define _LINUX_ETHTOOL_NETLINK_H_ + +#include <linux/ethtool.h> + +/* message types - userspace to kernel */ +enum { + ETHTOOL_MSG_USER_NONE, + ETHTOOL_MSG_STRSET_GET, + ETHTOOL_MSG_LINKINFO_GET, + ETHTOOL_MSG_LINKINFO_SET, + ETHTOOL_MSG_LINKMODES_GET, + ETHTOOL_MSG_LINKMODES_SET, + ETHTOOL_MSG_LINKSTATE_GET, + ETHTOOL_MSG_DEBUG_GET, + 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, + ETHTOOL_MSG_FEC_GET, + ETHTOOL_MSG_FEC_SET, + ETHTOOL_MSG_MODULE_EEPROM_GET, + ETHTOOL_MSG_STATS_GET, + ETHTOOL_MSG_PHC_VCLOCKS_GET, + ETHTOOL_MSG_MODULE_GET, + ETHTOOL_MSG_MODULE_SET, + ETHTOOL_MSG_PSE_GET, + ETHTOOL_MSG_PSE_SET, + ETHTOOL_MSG_RSS_GET, + ETHTOOL_MSG_PLCA_GET_CFG, + ETHTOOL_MSG_PLCA_SET_CFG, + ETHTOOL_MSG_PLCA_GET_STATUS, + ETHTOOL_MSG_MM_GET, + ETHTOOL_MSG_MM_SET, + + /* add new constants above here */ + __ETHTOOL_MSG_USER_CNT, + ETHTOOL_MSG_USER_MAX = __ETHTOOL_MSG_USER_CNT - 1 +}; + +/* message types - kernel to userspace */ +enum { + ETHTOOL_MSG_KERNEL_NONE, + ETHTOOL_MSG_STRSET_GET_REPLY, + ETHTOOL_MSG_LINKINFO_GET_REPLY, + ETHTOOL_MSG_LINKINFO_NTF, + ETHTOOL_MSG_LINKMODES_GET_REPLY, + ETHTOOL_MSG_LINKMODES_NTF, + ETHTOOL_MSG_LINKSTATE_GET_REPLY, + ETHTOOL_MSG_DEBUG_GET_REPLY, + 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, + ETHTOOL_MSG_FEC_GET_REPLY, + ETHTOOL_MSG_FEC_NTF, + ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY, + ETHTOOL_MSG_STATS_GET_REPLY, + ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY, + ETHTOOL_MSG_MODULE_GET_REPLY, + ETHTOOL_MSG_MODULE_NTF, + ETHTOOL_MSG_PSE_GET_REPLY, + ETHTOOL_MSG_RSS_GET_REPLY, + ETHTOOL_MSG_PLCA_GET_CFG_REPLY, + ETHTOOL_MSG_PLCA_GET_STATUS_REPLY, + ETHTOOL_MSG_PLCA_NTF, + ETHTOOL_MSG_MM_GET_REPLY, + ETHTOOL_MSG_MM_NTF, + + /* add new constants above here */ + __ETHTOOL_MSG_KERNEL_CNT, + ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1 +}; + +/* request header */ + +/* use compact bitsets in reply */ +#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_STATS) + +enum { + ETHTOOL_A_HEADER_UNSPEC, + ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */ + ETHTOOL_A_HEADER_DEV_NAME, /* string */ + ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */ + + /* add new constants above here */ + __ETHTOOL_A_HEADER_CNT, + ETHTOOL_A_HEADER_MAX = __ETHTOOL_A_HEADER_CNT - 1 +}; + +/* bit sets */ + +enum { + ETHTOOL_A_BITSET_BIT_UNSPEC, + ETHTOOL_A_BITSET_BIT_INDEX, /* u32 */ + ETHTOOL_A_BITSET_BIT_NAME, /* string */ + ETHTOOL_A_BITSET_BIT_VALUE, /* flag */ + + /* add new constants above here */ + __ETHTOOL_A_BITSET_BIT_CNT, + ETHTOOL_A_BITSET_BIT_MAX = __ETHTOOL_A_BITSET_BIT_CNT - 1 +}; + +enum { + ETHTOOL_A_BITSET_BITS_UNSPEC, + ETHTOOL_A_BITSET_BITS_BIT, /* nest - _A_BITSET_BIT_* */ + + /* add new constants above here */ + __ETHTOOL_A_BITSET_BITS_CNT, + ETHTOOL_A_BITSET_BITS_MAX = __ETHTOOL_A_BITSET_BITS_CNT - 1 +}; + +enum { + ETHTOOL_A_BITSET_UNSPEC, + ETHTOOL_A_BITSET_NOMASK, /* flag */ + ETHTOOL_A_BITSET_SIZE, /* u32 */ + ETHTOOL_A_BITSET_BITS, /* nest - _A_BITSET_BITS_* */ + ETHTOOL_A_BITSET_VALUE, /* binary */ + ETHTOOL_A_BITSET_MASK, /* binary */ + + /* add new constants above here */ + __ETHTOOL_A_BITSET_CNT, + ETHTOOL_A_BITSET_MAX = __ETHTOOL_A_BITSET_CNT - 1 +}; + +/* string sets */ + +enum { + ETHTOOL_A_STRING_UNSPEC, + ETHTOOL_A_STRING_INDEX, /* u32 */ + ETHTOOL_A_STRING_VALUE, /* string */ + + /* add new constants above here */ + __ETHTOOL_A_STRING_CNT, + ETHTOOL_A_STRING_MAX = __ETHTOOL_A_STRING_CNT - 1 +}; + +enum { + ETHTOOL_A_STRINGS_UNSPEC, + ETHTOOL_A_STRINGS_STRING, /* nest - _A_STRINGS_* */ + + /* add new constants above here */ + __ETHTOOL_A_STRINGS_CNT, + ETHTOOL_A_STRINGS_MAX = __ETHTOOL_A_STRINGS_CNT - 1 +}; + +enum { + ETHTOOL_A_STRINGSET_UNSPEC, + ETHTOOL_A_STRINGSET_ID, /* u32 */ + ETHTOOL_A_STRINGSET_COUNT, /* u32 */ + ETHTOOL_A_STRINGSET_STRINGS, /* nest - _A_STRINGS_* */ + + /* add new constants above here */ + __ETHTOOL_A_STRINGSET_CNT, + ETHTOOL_A_STRINGSET_MAX = __ETHTOOL_A_STRINGSET_CNT - 1 +}; + +enum { + ETHTOOL_A_STRINGSETS_UNSPEC, + ETHTOOL_A_STRINGSETS_STRINGSET, /* nest - _A_STRINGSET_* */ + + /* add new constants above here */ + __ETHTOOL_A_STRINGSETS_CNT, + ETHTOOL_A_STRINGSETS_MAX = __ETHTOOL_A_STRINGSETS_CNT - 1 +}; + +/* STRSET */ + +enum { + ETHTOOL_A_STRSET_UNSPEC, + ETHTOOL_A_STRSET_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_STRSET_STRINGSETS, /* nest - _A_STRINGSETS_* */ + ETHTOOL_A_STRSET_COUNTS_ONLY, /* flag */ + + /* add new constants above here */ + __ETHTOOL_A_STRSET_CNT, + ETHTOOL_A_STRSET_MAX = __ETHTOOL_A_STRSET_CNT - 1 +}; + +/* LINKINFO */ + +enum { + ETHTOOL_A_LINKINFO_UNSPEC, + ETHTOOL_A_LINKINFO_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_LINKINFO_PORT, /* u8 */ + ETHTOOL_A_LINKINFO_PHYADDR, /* u8 */ + ETHTOOL_A_LINKINFO_TP_MDIX, /* u8 */ + ETHTOOL_A_LINKINFO_TP_MDIX_CTRL, /* u8 */ + ETHTOOL_A_LINKINFO_TRANSCEIVER, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_LINKINFO_CNT, + ETHTOOL_A_LINKINFO_MAX = __ETHTOOL_A_LINKINFO_CNT - 1 +}; + +/* LINKMODES */ + +enum { + ETHTOOL_A_LINKMODES_UNSPEC, + ETHTOOL_A_LINKMODES_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_LINKMODES_AUTONEG, /* u8 */ + ETHTOOL_A_LINKMODES_OURS, /* bitset */ + 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 */ + ETHTOOL_A_LINKMODES_LANES, /* u32 */ + ETHTOOL_A_LINKMODES_RATE_MATCHING, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_LINKMODES_CNT, + ETHTOOL_A_LINKMODES_MAX = __ETHTOOL_A_LINKMODES_CNT - 1 +}; + +/* LINKSTATE */ + +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 */ + ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_LINKSTATE_CNT, + ETHTOOL_A_LINKSTATE_MAX = __ETHTOOL_A_LINKSTATE_CNT - 1 +}; + +/* DEBUG */ + +enum { + ETHTOOL_A_DEBUG_UNSPEC, + ETHTOOL_A_DEBUG_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_DEBUG_MSGMASK, /* bitset */ + + /* add new constants above here */ + __ETHTOOL_A_DEBUG_CNT, + ETHTOOL_A_DEBUG_MAX = __ETHTOOL_A_DEBUG_CNT - 1 +}; + +/* WOL */ + +enum { + ETHTOOL_A_WOL_UNSPEC, + ETHTOOL_A_WOL_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_WOL_MODES, /* bitset */ + ETHTOOL_A_WOL_SOPASS, /* binary */ + + /* add new constants above here */ + __ETHTOOL_A_WOL_CNT, + 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_TCP_DATA_SPLIT_UNKNOWN = 0, + ETHTOOL_TCP_DATA_SPLIT_DISABLED, + ETHTOOL_TCP_DATA_SPLIT_ENABLED, +}; + +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 */ + ETHTOOL_A_RINGS_RX_BUF_LEN, /* u32 */ + ETHTOOL_A_RINGS_TCP_DATA_SPLIT, /* u8 */ + ETHTOOL_A_RINGS_CQE_SIZE, /* u32 */ + ETHTOOL_A_RINGS_TX_PUSH, /* u8 */ + ETHTOOL_A_RINGS_RX_PUSH, /* u8 */ + ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN, /* u32 */ + ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX, /* 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 */ + ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, /* u8 */ + ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, /* u8 */ + ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES, /* u32 */ + ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, /* u32 */ + ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, /* 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_* */ + ETHTOOL_A_PAUSE_STATS_SRC, /* u32 */ + + /* 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 + * adjust ETHTOOL_PAUSE_STAT_CNT if adding non-stats! + */ + __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) +}; + +/* PHC VCLOCKS */ + +enum { + ETHTOOL_A_PHC_VCLOCKS_UNSPEC, + ETHTOOL_A_PHC_VCLOCKS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_PHC_VCLOCKS_NUM, /* u32 */ + ETHTOOL_A_PHC_VCLOCKS_INDEX, /* array, s32 */ + + /* add new constants above here */ + __ETHTOOL_A_PHC_VCLOCKS_CNT, + ETHTOOL_A_PHC_VCLOCKS_MAX = (__ETHTOOL_A_PHC_VCLOCKS_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) +}; + +/* FEC */ + +enum { + ETHTOOL_A_FEC_UNSPEC, + ETHTOOL_A_FEC_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_FEC_MODES, /* bitset */ + ETHTOOL_A_FEC_AUTO, /* u8 */ + ETHTOOL_A_FEC_ACTIVE, /* u32 */ + ETHTOOL_A_FEC_STATS, /* nest - _A_FEC_STAT */ + + __ETHTOOL_A_FEC_CNT, + ETHTOOL_A_FEC_MAX = (__ETHTOOL_A_FEC_CNT - 1) +}; + +enum { + ETHTOOL_A_FEC_STAT_UNSPEC, + ETHTOOL_A_FEC_STAT_PAD, + + ETHTOOL_A_FEC_STAT_CORRECTED, /* array, u64 */ + ETHTOOL_A_FEC_STAT_UNCORR, /* array, u64 */ + ETHTOOL_A_FEC_STAT_CORR_BITS, /* array, u64 */ + + /* add new constants above here */ + __ETHTOOL_A_FEC_STAT_CNT, + ETHTOOL_A_FEC_STAT_MAX = (__ETHTOOL_A_FEC_STAT_CNT - 1) +}; + +/* MODULE EEPROM */ + +enum { + ETHTOOL_A_MODULE_EEPROM_UNSPEC, + ETHTOOL_A_MODULE_EEPROM_HEADER, /* nest - _A_HEADER_* */ + + ETHTOOL_A_MODULE_EEPROM_OFFSET, /* u32 */ + ETHTOOL_A_MODULE_EEPROM_LENGTH, /* u32 */ + ETHTOOL_A_MODULE_EEPROM_PAGE, /* u8 */ + ETHTOOL_A_MODULE_EEPROM_BANK, /* u8 */ + ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, /* u8 */ + ETHTOOL_A_MODULE_EEPROM_DATA, /* binary */ + + __ETHTOOL_A_MODULE_EEPROM_CNT, + ETHTOOL_A_MODULE_EEPROM_MAX = (__ETHTOOL_A_MODULE_EEPROM_CNT - 1) +}; + +/* STATS */ + +enum { + ETHTOOL_A_STATS_UNSPEC, + ETHTOOL_A_STATS_PAD, + ETHTOOL_A_STATS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_STATS_GROUPS, /* bitset */ + + ETHTOOL_A_STATS_GRP, /* nest - _A_STATS_GRP_* */ + + ETHTOOL_A_STATS_SRC, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_STATS_CNT, + ETHTOOL_A_STATS_MAX = (__ETHTOOL_A_STATS_CNT - 1) +}; + +enum { + ETHTOOL_STATS_ETH_PHY, + ETHTOOL_STATS_ETH_MAC, + ETHTOOL_STATS_ETH_CTRL, + ETHTOOL_STATS_RMON, + + /* add new constants above here */ + __ETHTOOL_STATS_CNT +}; + +enum { + ETHTOOL_A_STATS_GRP_UNSPEC, + ETHTOOL_A_STATS_GRP_PAD, + + ETHTOOL_A_STATS_GRP_ID, /* u32 */ + ETHTOOL_A_STATS_GRP_SS_ID, /* u32 */ + + ETHTOOL_A_STATS_GRP_STAT, /* nest */ + + ETHTOOL_A_STATS_GRP_HIST_RX, /* nest */ + ETHTOOL_A_STATS_GRP_HIST_TX, /* nest */ + + ETHTOOL_A_STATS_GRP_HIST_BKT_LOW, /* u32 */ + ETHTOOL_A_STATS_GRP_HIST_BKT_HI, /* u32 */ + ETHTOOL_A_STATS_GRP_HIST_VAL, /* u64 */ + + /* add new constants above here */ + __ETHTOOL_A_STATS_GRP_CNT, + ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_GRP_CNT - 1) +}; + +enum { + /* 30.3.2.1.5 aSymbolErrorDuringCarrier */ + ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR, + + /* add new constants above here */ + __ETHTOOL_A_STATS_ETH_PHY_CNT, + ETHTOOL_A_STATS_ETH_PHY_MAX = (__ETHTOOL_A_STATS_ETH_PHY_CNT - 1) +}; + +enum { + /* 30.3.1.1.2 aFramesTransmittedOK */ + ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT, + /* 30.3.1.1.3 aSingleCollisionFrames */ + ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL, + /* 30.3.1.1.4 aMultipleCollisionFrames */ + ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL, + /* 30.3.1.1.5 aFramesReceivedOK */ + ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT, + /* 30.3.1.1.6 aFrameCheckSequenceErrors */ + ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR, + /* 30.3.1.1.7 aAlignmentErrors */ + ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR, + /* 30.3.1.1.8 aOctetsTransmittedOK */ + ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES, + /* 30.3.1.1.9 aFramesWithDeferredXmissions */ + ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER, + /* 30.3.1.1.10 aLateCollisions */ + ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL, + /* 30.3.1.1.11 aFramesAbortedDueToXSColls */ + ETHTOOL_A_STATS_ETH_MAC_11_XS_COL, + /* 30.3.1.1.12 aFramesLostDueToIntMACXmitError */ + ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR, + /* 30.3.1.1.13 aCarrierSenseErrors */ + ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR, + /* 30.3.1.1.14 aOctetsReceivedOK */ + ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES, + /* 30.3.1.1.15 aFramesLostDueToIntMACRcvError */ + ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR, + + /* 30.3.1.1.18 aMulticastFramesXmittedOK */ + ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST, + /* 30.3.1.1.19 aBroadcastFramesXmittedOK */ + ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST, + /* 30.3.1.1.20 aFramesWithExcessiveDeferral */ + ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER, + /* 30.3.1.1.21 aMulticastFramesReceivedOK */ + ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST, + /* 30.3.1.1.22 aBroadcastFramesReceivedOK */ + ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST, + /* 30.3.1.1.23 aInRangeLengthErrors */ + ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR, + /* 30.3.1.1.24 aOutOfRangeLengthField */ + ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN, + /* 30.3.1.1.25 aFrameTooLongErrors */ + ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR, + + /* add new constants above here */ + __ETHTOOL_A_STATS_ETH_MAC_CNT, + ETHTOOL_A_STATS_ETH_MAC_MAX = (__ETHTOOL_A_STATS_ETH_MAC_CNT - 1) +}; + +enum { + /* 30.3.3.3 aMACControlFramesTransmitted */ + ETHTOOL_A_STATS_ETH_CTRL_3_TX, + /* 30.3.3.4 aMACControlFramesReceived */ + ETHTOOL_A_STATS_ETH_CTRL_4_RX, + /* 30.3.3.5 aUnsupportedOpcodesReceived */ + ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP, + + /* add new constants above here */ + __ETHTOOL_A_STATS_ETH_CTRL_CNT, + ETHTOOL_A_STATS_ETH_CTRL_MAX = (__ETHTOOL_A_STATS_ETH_CTRL_CNT - 1) +}; + +enum { + /* etherStatsUndersizePkts */ + ETHTOOL_A_STATS_RMON_UNDERSIZE, + /* etherStatsOversizePkts */ + ETHTOOL_A_STATS_RMON_OVERSIZE, + /* etherStatsFragments */ + ETHTOOL_A_STATS_RMON_FRAG, + /* etherStatsJabbers */ + ETHTOOL_A_STATS_RMON_JABBER, + + /* add new constants above here */ + __ETHTOOL_A_STATS_RMON_CNT, + ETHTOOL_A_STATS_RMON_MAX = (__ETHTOOL_A_STATS_RMON_CNT - 1) +}; + +/* MODULE */ + +enum { + ETHTOOL_A_MODULE_UNSPEC, + ETHTOOL_A_MODULE_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_MODULE_POWER_MODE_POLICY, /* u8 */ + ETHTOOL_A_MODULE_POWER_MODE, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_MODULE_CNT, + ETHTOOL_A_MODULE_MAX = (__ETHTOOL_A_MODULE_CNT - 1) +}; + +/* Power Sourcing Equipment */ +enum { + ETHTOOL_A_PSE_UNSPEC, + ETHTOOL_A_PSE_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_PODL_PSE_ADMIN_STATE, /* u32 */ + ETHTOOL_A_PODL_PSE_ADMIN_CONTROL, /* u32 */ + ETHTOOL_A_PODL_PSE_PW_D_STATUS, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_PSE_CNT, + ETHTOOL_A_PSE_MAX = (__ETHTOOL_A_PSE_CNT - 1) +}; + +enum { + ETHTOOL_A_RSS_UNSPEC, + ETHTOOL_A_RSS_HEADER, + ETHTOOL_A_RSS_CONTEXT, /* u32 */ + ETHTOOL_A_RSS_HFUNC, /* u32 */ + ETHTOOL_A_RSS_INDIR, /* binary */ + ETHTOOL_A_RSS_HKEY, /* binary */ + + __ETHTOOL_A_RSS_CNT, + ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_CNT - 1), +}; + +/* PLCA */ + +enum { + ETHTOOL_A_PLCA_UNSPEC, + ETHTOOL_A_PLCA_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_PLCA_VERSION, /* u16 */ + ETHTOOL_A_PLCA_ENABLED, /* u8 */ + ETHTOOL_A_PLCA_STATUS, /* u8 */ + ETHTOOL_A_PLCA_NODE_CNT, /* u32 */ + ETHTOOL_A_PLCA_NODE_ID, /* u32 */ + ETHTOOL_A_PLCA_TO_TMR, /* u32 */ + ETHTOOL_A_PLCA_BURST_CNT, /* u32 */ + ETHTOOL_A_PLCA_BURST_TMR, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_PLCA_CNT, + ETHTOOL_A_PLCA_MAX = (__ETHTOOL_A_PLCA_CNT - 1) +}; + +/* MAC Merge (802.3) */ + +enum { + ETHTOOL_A_MM_STAT_UNSPEC, + ETHTOOL_A_MM_STAT_PAD, + + /* aMACMergeFrameAssErrorCount */ + ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS, /* u64 */ + /* aMACMergeFrameSmdErrorCount */ + ETHTOOL_A_MM_STAT_SMD_ERRORS, /* u64 */ + /* aMACMergeFrameAssOkCount */ + ETHTOOL_A_MM_STAT_REASSEMBLY_OK, /* u64 */ + /* aMACMergeFragCountRx */ + ETHTOOL_A_MM_STAT_RX_FRAG_COUNT, /* u64 */ + /* aMACMergeFragCountTx */ + ETHTOOL_A_MM_STAT_TX_FRAG_COUNT, /* u64 */ + /* aMACMergeHoldCount */ + ETHTOOL_A_MM_STAT_HOLD_COUNT, /* u64 */ + + /* add new constants above here */ + __ETHTOOL_A_MM_STAT_CNT, + ETHTOOL_A_MM_STAT_MAX = (__ETHTOOL_A_MM_STAT_CNT - 1) +}; + +enum { + ETHTOOL_A_MM_UNSPEC, + ETHTOOL_A_MM_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_MM_PMAC_ENABLED, /* u8 */ + ETHTOOL_A_MM_TX_ENABLED, /* u8 */ + ETHTOOL_A_MM_TX_ACTIVE, /* u8 */ + ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, /* u32 */ + ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, /* u32 */ + ETHTOOL_A_MM_VERIFY_ENABLED, /* u8 */ + ETHTOOL_A_MM_VERIFY_STATUS, /* u8 */ + ETHTOOL_A_MM_VERIFY_TIME, /* u32 */ + ETHTOOL_A_MM_MAX_VERIFY_TIME, /* u32 */ + ETHTOOL_A_MM_STATS, /* nest - _A_MM_STAT_* */ + + /* add new constants above here */ + __ETHTOOL_A_MM_CNT, + ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_CNT - 1) +}; + +/* generic netlink info */ +#define ETHTOOL_GENL_NAME "ethtool" +#define ETHTOOL_GENL_VERSION 1 + +#define ETHTOOL_MCGRP_MONITOR_NAME "monitor" + +#endif /* _LINUX_ETHTOOL_NETLINK_H_ */ diff --git a/uapi/linux/genetlink.h b/uapi/linux/genetlink.h new file mode 100644 index 0000000..e9b8117 --- /dev/null +++ b/uapi/linux/genetlink.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include <linux/types.h> +#include <linux/netlink.h> + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +#define GENL_ADMIN_PERM 0x01 +#define GENL_CMD_CAP_DO 0x02 +#define GENL_CMD_CAP_DUMP 0x04 +#define GENL_CMD_CAP_HASPOL 0x08 +#define GENL_UNS_ADMIN_PERM 0x10 + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_CTRL NLMSG_MIN_TYPE +#define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1) +#define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2) +/* must be last reserved + 1 */ +#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3) + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + CTRL_CMD_NEWMCAST_GRP, + CTRL_CMD_DELMCAST_GRP, + CTRL_CMD_GETMCAST_GRP, /* unused */ + CTRL_CMD_GETPOLICY, + __CTRL_CMD_MAX, +}; + +#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) + +enum { + CTRL_ATTR_UNSPEC, + CTRL_ATTR_FAMILY_ID, + CTRL_ATTR_FAMILY_NAME, + CTRL_ATTR_VERSION, + CTRL_ATTR_HDRSIZE, + CTRL_ATTR_MAXATTR, + CTRL_ATTR_OPS, + CTRL_ATTR_MCAST_GROUPS, + CTRL_ATTR_POLICY, + CTRL_ATTR_OP_POLICY, + CTRL_ATTR_OP, + __CTRL_ATTR_MAX, +}; + +#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1) + +enum { + CTRL_ATTR_OP_UNSPEC, + CTRL_ATTR_OP_ID, + CTRL_ATTR_OP_FLAGS, + __CTRL_ATTR_OP_MAX, +}; + +#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) + +enum { + CTRL_ATTR_MCAST_GRP_UNSPEC, + CTRL_ATTR_MCAST_GRP_NAME, + CTRL_ATTR_MCAST_GRP_ID, + __CTRL_ATTR_MCAST_GRP_MAX, +}; + +#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) + +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_POLICY_MAX (__CTRL_ATTR_POLICY_DUMP_MAX - 1) + +#endif /* __LINUX_GENERIC_NETLINK_H */ diff --git a/uapi/linux/hdlc/ioctl.h b/uapi/linux/hdlc/ioctl.h new file mode 100644 index 0000000..b06341a --- /dev/null +++ b/uapi/linux/hdlc/ioctl.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __HDLC_IOCTL_H__ +#define __HDLC_IOCTL_H__ + + +#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */ + +#define CLOCK_DEFAULT 0 /* Default setting */ +#define CLOCK_EXT 1 /* External TX and RX clock - DTE */ +#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */ +#define CLOCK_TXINT 3 /* Internal TX and external RX clock */ +#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */ + + +#define ENCODING_DEFAULT 0 /* Default setting */ +#define ENCODING_NRZ 1 +#define ENCODING_NRZI 2 +#define ENCODING_FM_MARK 3 +#define ENCODING_FM_SPACE 4 +#define ENCODING_MANCHESTER 5 + + +#define PARITY_DEFAULT 0 /* Default setting */ +#define PARITY_NONE 1 /* No parity */ +#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */ +#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */ +#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */ +#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */ +#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */ +#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */ + +#define LMI_DEFAULT 0 /* Default setting */ +#define LMI_NONE 1 /* No LMI, all PVCs are static */ +#define LMI_ANSI 2 /* ANSI Annex D */ +#define LMI_CCITT 3 /* ITU-T Annex A */ +#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */ + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; +} sync_serial_settings; /* V.35, V.24, X.21 */ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; + unsigned int slot_map; +} te1_settings; /* T1, E1 */ + +typedef struct { + unsigned short encoding; + unsigned short parity; +} raw_hdlc_proto; + +typedef struct { + unsigned int t391; + unsigned int t392; + unsigned int n391; + unsigned int n392; + unsigned int n393; + unsigned short lmi; + unsigned short dce; /* 1 for DCE (network side) operation */ +} fr_proto; + +typedef struct { + unsigned int dlci; +} fr_proto_pvc; /* for creating/deleting FR PVCs */ + +typedef struct { + unsigned int dlci; + char master[IFNAMSIZ]; /* Name of master FRAD device */ +}fr_proto_pvc_info; /* for returning PVC information only */ + +typedef struct { + unsigned int interval; + unsigned int timeout; +} cisco_proto; + +typedef struct { + unsigned short dce; /* 1 for DCE (network side) operation */ + unsigned int modulo; /* modulo (8 = basic / 128 = extended) */ + unsigned int window; /* frame window size */ + unsigned int t1; /* timeout t1 */ + unsigned int t2; /* timeout t2 */ + unsigned int n2; /* frame retry counter */ +} x25_hdlc_proto; + +/* PPP doesn't need any info now - supply length = 0 to ioctl */ + +#endif /* __ASSEMBLY__ */ +#endif /* __HDLC_IOCTL_H__ */ diff --git a/uapi/linux/if.h b/uapi/linux/if.h new file mode 100644 index 0000000..b287b2a --- /dev/null +++ b/uapi/linux/if.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the INET interface module. + * + * Version: @(#)if.h 1.0.2 04/18/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988 + * Ross Biro + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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. + */ +#ifndef _LINUX_IF_H +#define _LINUX_IF_H + +#include <linux/libc-compat.h> /* for compatibility with glibc */ +#include <linux/types.h> /* for "__kernel_caddr_t" et al */ +#include <linux/socket.h> /* for "struct sockaddr" et al */ + /* for "__user" et al */ + +#include <sys/socket.h> /* for struct sockaddr. */ + +#if __UAPI_DEF_IF_IFNAMSIZ +#define IFNAMSIZ 16 +#endif /* __UAPI_DEF_IF_IFNAMSIZ */ +#define IFALIASZ 256 +#define ALTIFNAMSIZ 128 +#include <linux/hdlc/ioctl.h> + +/* For glibc compatibility. An empty enum does not compile. */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \ + __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 +/** + * enum net_device_flags - &struct net_device flags + * + * These are the &struct net_device flags, they can be set by drivers, the + * kernel and some can be triggered by userspace. Userspace can query and + * set these flags using userspace utilities but there is also a sysfs + * entry available for all dev flags which can be queried and set. These flags + * are shared for all types of net_devices. The sysfs entries are available + * via /sys/class/net/<dev>/flags. Flags which can be toggled through sysfs + * are annotated below, note that only a few flags can be toggled and some + * other flags are always preserved from the original net_device flags + * even if you try to set them via sysfs. Flags which are always preserved + * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__ + * are annotated below as such. + * + * You should have a pretty good reason to be extending these flags. + * + * @IFF_UP: interface is up. Can be toggled through sysfs. + * @IFF_BROADCAST: broadcast address valid. Volatile. + * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs. + * @IFF_LOOPBACK: is a loopback net. Volatile. + * @IFF_POINTOPOINT: interface is has p-p link. Volatile. + * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs. + * Volatile. + * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile. + * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile. + * @IFF_PROMISC: receive all packets. Can be toggled through sysfs. + * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through + * sysfs. + * @IFF_MASTER: master of a load balancer. Volatile. + * @IFF_SLAVE: slave of a load balancer. Volatile. + * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs. + * @IFF_PORTSEL: can set media type. Can be toggled through sysfs. + * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs. + * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled + * through sysfs. + * @IFF_LOWER_UP: driver signals L1 up. Volatile. + * @IFF_DORMANT: driver signals dormant. Volatile. + * @IFF_ECHO: echo sent packets. Volatile. + */ +enum net_device_flags { +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS + IFF_UP = 1<<0, /* sysfs */ + IFF_BROADCAST = 1<<1, /* __volatile__ */ + IFF_DEBUG = 1<<2, /* sysfs */ + IFF_LOOPBACK = 1<<3, /* __volatile__ */ + IFF_POINTOPOINT = 1<<4, /* __volatile__ */ + IFF_NOTRAILERS = 1<<5, /* sysfs */ + IFF_RUNNING = 1<<6, /* __volatile__ */ + IFF_NOARP = 1<<7, /* sysfs */ + IFF_PROMISC = 1<<8, /* sysfs */ + IFF_ALLMULTI = 1<<9, /* sysfs */ + IFF_MASTER = 1<<10, /* __volatile__ */ + IFF_SLAVE = 1<<11, /* __volatile__ */ + IFF_MULTICAST = 1<<12, /* sysfs */ + IFF_PORTSEL = 1<<13, /* sysfs */ + IFF_AUTOMEDIA = 1<<14, /* sysfs */ + IFF_DYNAMIC = 1<<15, /* sysfs */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO + IFF_LOWER_UP = 1<<16, /* __volatile__ */ + IFF_DORMANT = 1<<17, /* __volatile__ */ + IFF_ECHO = 1<<18, /* __volatile__ */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ +}; +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define IFF_UP IFF_UP +#define IFF_BROADCAST IFF_BROADCAST +#define IFF_DEBUG IFF_DEBUG +#define IFF_LOOPBACK IFF_LOOPBACK +#define IFF_POINTOPOINT IFF_POINTOPOINT +#define IFF_NOTRAILERS IFF_NOTRAILERS +#define IFF_RUNNING IFF_RUNNING +#define IFF_NOARP IFF_NOARP +#define IFF_PROMISC IFF_PROMISC +#define IFF_ALLMULTI IFF_ALLMULTI +#define IFF_MASTER IFF_MASTER +#define IFF_SLAVE IFF_SLAVE +#define IFF_MULTICAST IFF_MULTICAST +#define IFF_PORTSEL IFF_PORTSEL +#define IFF_AUTOMEDIA IFF_AUTOMEDIA +#define IFF_DYNAMIC IFF_DYNAMIC +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ + +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define IFF_LOWER_UP IFF_LOWER_UP +#define IFF_DORMANT IFF_DORMANT +#define IFF_ECHO IFF_ECHO +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ + IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) + +#define IF_GET_IFACE 0x0001 /* for querying only */ +#define IF_GET_PROTO 0x0002 + +/* For definitions see hdlc.h */ +#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ +#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ +#define IF_IFACE_X21 0x1002 /* X.21 serial interface */ +#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ +#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ +#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */ +#define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */ + +/* For definitions see hdlc.h */ +#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ +#define IF_PROTO_PPP 0x2001 /* PPP protocol */ +#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ +#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ +#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ +#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ +#define IF_PROTO_X25 0x2006 /* X.25 */ +#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */ +#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */ +#define IF_PROTO_FR_ETH_PVC 0x200B +#define IF_PROTO_RAW 0x200C /* RAW Socket */ + +/* RFC 2863 operational status */ +enum { + IF_OPER_UNKNOWN, + IF_OPER_NOTPRESENT, + IF_OPER_DOWN, + IF_OPER_LOWERLAYERDOWN, + IF_OPER_TESTING, + IF_OPER_DORMANT, + IF_OPER_UP, +}; + +/* link modes */ +enum { + IF_LINK_MODE_DEFAULT, + IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ + IF_LINK_MODE_TESTING, /* limit upward transition to testing */ +}; + +/* + * Device mapping structure. I'd just gone off and designed a + * beautiful scheme using only loadable modules with arguments + * for driver options and along come the PCMCIA people 8) + * + * Ah well. The get() side of this is good for WDSETUP, and it'll + * be handy for debugging things. The set side is fine for now and + * being very small might be worth keeping for clean configuration. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFMAP +struct ifmap { + unsigned long mem_start; + unsigned long mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; + /* 3 bytes spare */ +}; +#endif /* __UAPI_DEF_IF_IFMAP */ + +struct if_settings { + unsigned int type; /* Type of physical device or protocol */ + unsigned int size; /* Size of the data allocated by the caller */ + union { + /* {atm/eth/dsl}_settings anyone ? */ + raw_hdlc_proto *raw_hdlc; + cisco_proto *cisco; + fr_proto *fr; + fr_proto_pvc *fr_pvc; + fr_proto_pvc_info *fr_pvc_info; + x25_hdlc_proto *x25; + + /* interface settings */ + sync_serial_settings *sync; + te1_settings *te1; + } ifs_ifsu; +}; + +/* + * Interface request structure used for socket + * ioctl's. All interface ioctl's must have parameter + * definitions which begin with ifr_name. The + * remainder may be interface specific. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFREQ +struct ifreq { +#define IFHWADDRLEN 6 + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + void * ifru_data; + struct if_settings ifru_settings; + } ifr_ifru; +}; +#endif /* __UAPI_DEF_IF_IFREQ */ + +#define ifr_name ifr_ifrn.ifrn_name /* interface name */ +#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_metric ifr_ifru.ifru_ivalue /* metric */ +#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ +#define ifr_map ifr_ifru.ifru_map /* device map */ +#define ifr_slave ifr_ifru.ifru_slave /* slave device */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface */ +#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ +#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ +#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ +#define ifr_newname ifr_ifru.ifru_newname /* New name */ +#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ + +/* + * Structure used in SIOCGIFCONF request. + * Used to retrieve interface configuration + * for machine (useful for programs which + * must know all networks accessible). + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFCONF +struct ifconf { + int ifc_len; /* size of buffer */ + union { + char *ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +}; +#endif /* __UAPI_DEF_IF_IFCONF */ + +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures */ + +#endif /* _LINUX_IF_H */ diff --git a/uapi/linux/if_addr.h b/uapi/linux/if_addr.h new file mode 100644 index 0000000..d6db3ff --- /dev/null +++ b/uapi/linux/if_addr.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_IF_ADDR_H +#define __LINUX_IF_ADDR_H + +#include <linux/types.h> +#include <linux/netlink.h> + +struct ifaddrmsg { + __u8 ifa_family; + __u8 ifa_prefixlen; /* The prefix length */ + __u8 ifa_flags; /* Flags */ + __u8 ifa_scope; /* Address scope */ + __u32 ifa_index; /* Link index */ +}; + +/* + * Important comment: + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + * + * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags. + * If present, the value from struct ifaddrmsg will be ignored. + */ +enum { + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + IFA_FLAGS, + IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ + IFA_TARGET_NETNSID, + IFA_PROTO, /* u8, address protocol */ + __IFA_MAX, +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_NODAD 0x02 +#define IFA_F_OPTIMISTIC 0x04 +#define IFA_F_DADFAILED 0x08 +#define IFA_F_HOMEADDRESS 0x10 +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 +#define IFA_F_MANAGETEMPADDR 0x100 +#define IFA_F_NOPREFIXROUTE 0x200 +#define IFA_F_MCAUTOJOIN 0x400 +#define IFA_F_STABLE_PRIVACY 0x800 + +struct ifa_cacheinfo { + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + +/* backwards compatibility for userspace */ +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +/* ifa_proto */ +#define IFAPROT_UNSPEC 0 +#define IFAPROT_KERNEL_LO 1 /* loopback */ +#define IFAPROT_KERNEL_RA 2 /* set by kernel from router announcement */ +#define IFAPROT_KERNEL_LL 3 /* link-local set by kernel */ + +#endif diff --git a/uapi/linux/if_ether.h b/uapi/linux/if_ether.h new file mode 100644 index 0000000..a1aff8e --- /dev/null +++ b/uapi/linux/if_ether.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the Ethernet IEEE 802.3 interface. + * + * Version: @(#)if_ether.h 1.0.1a 02/08/94 + * + * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Donald Becker, <becker@super.org> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> + * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk> + * + * 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. + */ + +#ifndef _LINUX_IF_ETHER_H +#define _LINUX_IF_ETHER_H + +#include <linux/types.h> + +/* + * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble + * and FCS/CRC (frame check sequence). + */ + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_TLEN 2 /* Octets in ethernet type field */ +#define ETH_HLEN 14 /* Total octets in header. */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define ETH_DATA_LEN 1500 /* Max. octets in payload */ +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ +#define ETH_FCS_LEN 4 /* Octets in the FCS */ + +#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ +#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU */ + +/* + * These are the defined Ethernet Protocol ID's. + */ + +#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ +#define ETH_P_PUP 0x0200 /* Xerox PUP packet */ +#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ +#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */ +#define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */ +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_X25 0x0805 /* CCITT X.25 */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ +#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */ +#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */ +#define ETH_P_BATMAN 0x4305 /* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DEC 0x6000 /* DEC Assigned proto */ +#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */ +#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */ +#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */ +#define ETH_P_LAT 0x6004 /* DEC LAT */ +#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ +#define ETH_P_CUST 0x6006 /* DEC Customer use */ +#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ +#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */ +#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ +#define ETH_P_ATALK 0x809B /* Appletalk DDP */ +#define ETH_P_AARP 0x80F3 /* Appletalk AARP */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */ +#define ETH_P_IPX 0x8137 /* IPX over DIX */ +#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ +#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ +#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol + * defined in draft-wilson-wrec-wccp-v2-00.txt */ +#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ +#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ +#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ +#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ +#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ +#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport + * over Ethernet + */ +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_PROFINET 0x8892 /* PROFINET */ +#define ETH_P_REALTEK 0x8899 /* Multiple proprietary protocols */ +#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ +#define ETH_P_ETHERCAT 0x88A4 /* EtherCAT */ +#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ +#define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ +#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ +#define ETH_P_TIPC 0x88CA /* TIPC */ +#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ +#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */ +#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ +#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ +#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ +#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ +#define ETH_P_NCSI 0x88F8 /* NCSI protocol */ +#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ +#define ETH_P_CFM 0x8902 /* Connectivity Fault Management */ +#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */ +#define ETH_P_TDLS 0x890D /* TDLS */ +#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ +#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */ +#define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */ +#define ETH_P_NSH 0x894F /* Network Service Header */ +#define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */ +#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ +#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ + +#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value + * then the frame is Ethernet II. Else it is 802.3 */ + +/* + * Non DIX types. Won't clash for 1500 types. + */ + +#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */ +#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */ +#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */ +#define ETH_P_802_2 0x0004 /* 802.2 frames */ +#define ETH_P_SNAP 0x0005 /* Internal only */ +#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */ +#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/ +#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */ +#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */ +#define ETH_P_CAN 0x000C /* CAN: Controller Area Network */ +#define ETH_P_CANFD 0x000D /* CANFD: CAN flexible data rate*/ +#define ETH_P_CANXL 0x000E /* CANXL: eXtended frame Length */ +#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/ +#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */ +#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */ +#define ETH_P_CONTROL 0x0016 /* Card specific control frames */ +#define ETH_P_IRDA 0x0017 /* Linux-IrDA */ +#define ETH_P_ECONET 0x0018 /* Acorn Econet */ +#define ETH_P_HDLC 0x0019 /* HDLC frames */ +#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ +#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */ +#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */ +#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ +#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */ +#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */ +#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */ +#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and + * aggregation protocol + */ +#define ETH_P_MCTP 0x00FA /* Management component transport + * protocol packets + */ + +/* + * This is an Ethernet frame header. + */ + +/* allow libcs like musl to deactivate this, glibc does not implement this. */ +#ifndef __UAPI_DEF_ETHHDR +#define __UAPI_DEF_ETHHDR 1 +#endif + +#if __UAPI_DEF_ETHHDR +struct ethhdr { + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + __be16 h_proto; /* packet type ID field */ +} __attribute__((packed)); +#endif + + +#endif /* _LINUX_IF_ETHER_H */ diff --git a/uapi/linux/if_link.h b/uapi/linux/if_link.h new file mode 100644 index 0000000..02af33c --- /dev/null +++ b/uapi/linux/if_link.h @@ -0,0 +1,1398 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_IF_LINK_H +#define _LINUX_IF_LINK_H + +#include <linux/types.h> +#include <linux/netlink.h> + +/* This struct should be in sync with struct rtnl_link_stats64 */ +struct rtnl_link_stats { + __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; + __u32 rx_crc_errors; + __u32 rx_frame_errors; + __u32 rx_fifo_errors; + __u32 rx_missed_errors; + + /* detailed tx_errors */ + __u32 tx_aborted_errors; + __u32 tx_carrier_errors; + __u32 tx_fifo_errors; + __u32 tx_heartbeat_errors; + __u32 tx_window_errors; + + /* for cslip etc */ + __u32 rx_compressed; + __u32 tx_compressed; + + __u32 rx_nohandler; +}; + +/** + * 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 may include packets discarded + * due to L2 address filtering but should not include packets dropped + * by the device due to buffer exhaustion 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). + * + * @rx_otherhost_dropped: Number of packets dropped due to mismatch + * in destination MAC address. + */ +struct rtnl_link_stats64 { + __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; + __u64 rx_crc_errors; + __u64 rx_frame_errors; + __u64 rx_fifo_errors; + __u64 rx_missed_errors; + + /* detailed tx_errors */ + __u64 tx_aborted_errors; + __u64 tx_carrier_errors; + __u64 tx_fifo_errors; + __u64 tx_heartbeat_errors; + __u64 tx_window_errors; + + /* for cslip etc */ + __u64 rx_compressed; + __u64 tx_compressed; + __u64 rx_nohandler; + + __u64 rx_otherhost_dropped; +}; + +/* Subset of link stats useful for in-HW collection. Meaning of the fields is as + * for struct rtnl_link_stats64. + */ +struct rtnl_hw_stats64 { + __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; +}; + +/* The struct should be in sync with struct ifmap */ +struct rtnl_link_ifmap { + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +/* + * IFLA_AF_SPEC + * Contains nested attributes for address family specific attributes. + * Each address family may create a attribute with the address family + * number as type and create its own attribute structure in it. + * + * Example: + * [IFLA_AF_SPEC] = { + * [AF_INET] = { + * [IFLA_INET_CONF] = ..., + * }, + * [AF_INET6] = { + * [IFLA_INET6_FLAGS] = ..., + * [IFLA_INET6_CONF] = ..., + * } + * } + */ + +enum { + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + IFLA_OPERSTATE, + IFLA_LINKMODE, + IFLA_LINKINFO, +#define IFLA_LINKINFO IFLA_LINKINFO + IFLA_NET_NS_PID, + IFLA_IFALIAS, + IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */ + IFLA_VFINFO_LIST, + IFLA_STATS64, + IFLA_VF_PORTS, + IFLA_PORT_SELF, + IFLA_AF_SPEC, + IFLA_GROUP, /* Group the device belongs to */ + IFLA_NET_NS_FD, + IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ + IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ +#define IFLA_PROMISCUITY IFLA_PROMISCUITY + IFLA_NUM_TX_QUEUES, + IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, + IFLA_PHYS_PORT_ID, + IFLA_CARRIER_CHANGES, + IFLA_PHYS_SWITCH_ID, + IFLA_LINK_NETNSID, + IFLA_PHYS_PORT_NAME, + IFLA_PROTO_DOWN, + IFLA_GSO_MAX_SEGS, + IFLA_GSO_MAX_SIZE, + IFLA_PAD, + IFLA_XDP, + IFLA_EVENT, + IFLA_NEW_NETNSID, + IFLA_IF_NETNSID, + IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */ + IFLA_CARRIER_UP_COUNT, + IFLA_CARRIER_DOWN_COUNT, + IFLA_NEW_IFINDEX, + IFLA_MIN_MTU, + IFLA_MAX_MTU, + IFLA_PROP_LIST, + IFLA_ALT_IFNAME, /* Alternative ifname */ + IFLA_PERM_ADDRESS, + IFLA_PROTO_DOWN_REASON, + + /* device (sysfs) name as parent, used instead + * of IFLA_LINK where there's no parent netdev + */ + IFLA_PARENT_DEV_NAME, + IFLA_PARENT_DEV_BUS_NAME, + IFLA_GRO_MAX_SIZE, + IFLA_TSO_MAX_SIZE, + IFLA_TSO_MAX_SEGS, + IFLA_ALLMULTI, /* Allmulti count: > 0 means acts ALLMULTI */ + + IFLA_DEVLINK_PORT, + + IFLA_GSO_IPV4_MAX_SIZE, + IFLA_GRO_IPV4_MAX_SIZE, + + __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)) + +enum { + IFLA_INET_UNSPEC, + IFLA_INET_CONF, + __IFLA_INET_MAX, +}; + +#define IFLA_INET_MAX (__IFLA_INET_MAX - 1) + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neither of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/* Subtype attributes for IFLA_PROTINFO */ +enum { + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ + IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ + IFLA_INET6_TOKEN, /* device token */ + IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ + IFLA_INET6_RA_MTU, /* mtu carried in the RA message */ + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +enum in6_addr_gen_mode { + IN6_ADDR_GEN_MODE_EUI64, + IN6_ADDR_GEN_MODE_NONE, + IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IN6_ADDR_GEN_MODE_RANDOM, +}; + +/* Bridge section */ + +enum { + IFLA_BR_UNSPEC, + IFLA_BR_FORWARD_DELAY, + IFLA_BR_HELLO_TIME, + IFLA_BR_MAX_AGE, + IFLA_BR_AGEING_TIME, + IFLA_BR_STP_STATE, + IFLA_BR_PRIORITY, + IFLA_BR_VLAN_FILTERING, + IFLA_BR_VLAN_PROTOCOL, + IFLA_BR_GROUP_FWD_MASK, + IFLA_BR_ROOT_ID, + IFLA_BR_BRIDGE_ID, + IFLA_BR_ROOT_PORT, + IFLA_BR_ROOT_PATH_COST, + IFLA_BR_TOPOLOGY_CHANGE, + IFLA_BR_TOPOLOGY_CHANGE_DETECTED, + IFLA_BR_HELLO_TIMER, + IFLA_BR_TCN_TIMER, + IFLA_BR_TOPOLOGY_CHANGE_TIMER, + IFLA_BR_GC_TIMER, + IFLA_BR_GROUP_ADDR, + IFLA_BR_FDB_FLUSH, + IFLA_BR_MCAST_ROUTER, + IFLA_BR_MCAST_SNOOPING, + IFLA_BR_MCAST_QUERY_USE_IFADDR, + IFLA_BR_MCAST_QUERIER, + IFLA_BR_MCAST_HASH_ELASTICITY, + IFLA_BR_MCAST_HASH_MAX, + IFLA_BR_MCAST_LAST_MEMBER_CNT, + IFLA_BR_MCAST_STARTUP_QUERY_CNT, + IFLA_BR_MCAST_LAST_MEMBER_INTVL, + IFLA_BR_MCAST_MEMBERSHIP_INTVL, + IFLA_BR_MCAST_QUERIER_INTVL, + IFLA_BR_MCAST_QUERY_INTVL, + IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + IFLA_BR_NF_CALL_IPTABLES, + IFLA_BR_NF_CALL_IP6TABLES, + IFLA_BR_NF_CALL_ARPTABLES, + IFLA_BR_VLAN_DEFAULT_PVID, + IFLA_BR_PAD, + IFLA_BR_VLAN_STATS_ENABLED, + IFLA_BR_MCAST_STATS_ENABLED, + IFLA_BR_MCAST_IGMP_VERSION, + IFLA_BR_MCAST_MLD_VERSION, + IFLA_BR_VLAN_STATS_PER_PORT, + IFLA_BR_MULTI_BOOLOPT, + IFLA_BR_MCAST_QUERIER_STATE, + __IFLA_BR_MAX, +}; + +#define IFLA_BR_MAX (__IFLA_BR_MAX - 1) + +struct ifla_bridge_id { + __u8 prio[2]; + __u8 addr[6]; /* ETH_ALEN */ +}; + +enum { + BRIDGE_MODE_UNSPEC, + BRIDGE_MODE_HAIRPIN, +}; + +enum { + IFLA_BRPORT_UNSPEC, + IFLA_BRPORT_STATE, /* Spanning tree state */ + IFLA_BRPORT_PRIORITY, /* " priority */ + IFLA_BRPORT_COST, /* " cost */ + IFLA_BRPORT_MODE, /* mode (hairpin) */ + IFLA_BRPORT_GUARD, /* bpdu guard */ + IFLA_BRPORT_PROTECT, /* root port protection */ + IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ + IFLA_BRPORT_LEARNING, /* mac learning */ + IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */ + IFLA_BRPORT_PROXYARP, /* proxy ARP */ + IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ + IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ + IFLA_BRPORT_ROOT_ID, /* designated root */ + IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ + IFLA_BRPORT_DESIGNATED_PORT, + IFLA_BRPORT_DESIGNATED_COST, + IFLA_BRPORT_ID, + IFLA_BRPORT_NO, + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + IFLA_BRPORT_CONFIG_PENDING, + IFLA_BRPORT_MESSAGE_AGE_TIMER, + IFLA_BRPORT_FORWARD_DELAY_TIMER, + IFLA_BRPORT_HOLD_TIMER, + IFLA_BRPORT_FLUSH, + IFLA_BRPORT_MULTICAST_ROUTER, + IFLA_BRPORT_PAD, + IFLA_BRPORT_MCAST_FLOOD, + IFLA_BRPORT_MCAST_TO_UCAST, + IFLA_BRPORT_VLAN_TUNNEL, + IFLA_BRPORT_BCAST_FLOOD, + IFLA_BRPORT_GROUP_FWD_MASK, + IFLA_BRPORT_NEIGH_SUPPRESS, + IFLA_BRPORT_ISOLATED, + IFLA_BRPORT_BACKUP_PORT, + IFLA_BRPORT_MRP_RING_OPEN, + IFLA_BRPORT_MRP_IN_OPEN, + IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT, + IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, + IFLA_BRPORT_LOCKED, + IFLA_BRPORT_MAB, + IFLA_BRPORT_MCAST_N_GROUPS, + IFLA_BRPORT_MCAST_MAX_GROUPS, + IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + __IFLA_BRPORT_MAX +}; +#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) + +struct ifla_cacheinfo { + __u32 max_reasm_len; + __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ + __u32 reachable_time; + __u32 retrans_time; +}; + +enum { + IFLA_INFO_UNSPEC, + IFLA_INFO_KIND, + IFLA_INFO_DATA, + IFLA_INFO_XSTATS, + IFLA_INFO_SLAVE_KIND, + IFLA_INFO_SLAVE_DATA, + __IFLA_INFO_MAX, +}; + +#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1) + +/* VLAN section */ + +enum { + IFLA_VLAN_UNSPEC, + IFLA_VLAN_ID, + IFLA_VLAN_FLAGS, + IFLA_VLAN_EGRESS_QOS, + IFLA_VLAN_INGRESS_QOS, + IFLA_VLAN_PROTOCOL, + __IFLA_VLAN_MAX, +}; + +#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) + +struct ifla_vlan_flags { + __u32 flags; + __u32 mask; +}; + +enum { + IFLA_VLAN_QOS_UNSPEC, + IFLA_VLAN_QOS_MAPPING, + __IFLA_VLAN_QOS_MAX +}; + +#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1) + +struct ifla_vlan_qos_mapping { + __u32 from; + __u32 to; +}; + +/* MACVLAN section */ +enum { + IFLA_MACVLAN_UNSPEC, + IFLA_MACVLAN_MODE, + IFLA_MACVLAN_FLAGS, + IFLA_MACVLAN_MACADDR_MODE, + IFLA_MACVLAN_MACADDR, + IFLA_MACVLAN_MACADDR_DATA, + IFLA_MACVLAN_MACADDR_COUNT, + IFLA_MACVLAN_BC_QUEUE_LEN, + IFLA_MACVLAN_BC_QUEUE_LEN_USED, + IFLA_MACVLAN_BC_CUTOFF, + __IFLA_MACVLAN_MAX, +}; + +#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) + +enum macvlan_mode { + MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ + MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ + MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ + MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ + MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */ +}; + +enum macvlan_macaddr_mode { + MACVLAN_MACADDR_ADD, + MACVLAN_MACADDR_DEL, + MACVLAN_MACADDR_FLUSH, + MACVLAN_MACADDR_SET, +}; + +#define MACVLAN_FLAG_NOPROMISC 1 +#define MACVLAN_FLAG_NODST 2 /* skip dst macvlan if matching src macvlan */ + +/* VRF section */ +enum { + IFLA_VRF_UNSPEC, + IFLA_VRF_TABLE, + __IFLA_VRF_MAX +}; + +#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1) + +enum { + IFLA_VRF_PORT_UNSPEC, + IFLA_VRF_PORT_TABLE, + __IFLA_VRF_PORT_MAX +}; + +#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1) + +/* MACSEC section */ +enum { + IFLA_MACSEC_UNSPEC, + IFLA_MACSEC_SCI, + IFLA_MACSEC_PORT, + IFLA_MACSEC_ICV_LEN, + IFLA_MACSEC_CIPHER_SUITE, + IFLA_MACSEC_WINDOW, + IFLA_MACSEC_ENCODING_SA, + IFLA_MACSEC_ENCRYPT, + IFLA_MACSEC_PROTECT, + IFLA_MACSEC_INC_SCI, + IFLA_MACSEC_ES, + IFLA_MACSEC_SCB, + IFLA_MACSEC_REPLAY_PROTECT, + IFLA_MACSEC_VALIDATION, + IFLA_MACSEC_PAD, + IFLA_MACSEC_OFFLOAD, + __IFLA_MACSEC_MAX, +}; + +#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) + +/* XFRM section */ +enum { + IFLA_XFRM_UNSPEC, + IFLA_XFRM_LINK, + IFLA_XFRM_IF_ID, + IFLA_XFRM_COLLECT_METADATA, + __IFLA_XFRM_MAX +}; + +#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) + +enum macsec_validation_type { + MACSEC_VALIDATE_DISABLED = 0, + MACSEC_VALIDATE_CHECK = 1, + MACSEC_VALIDATE_STRICT = 2, + __MACSEC_VALIDATE_END, + MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, +}; + +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, +}; + +/* IPVLAN section */ +enum { + IFLA_IPVLAN_UNSPEC, + IFLA_IPVLAN_MODE, + IFLA_IPVLAN_FLAGS, + __IFLA_IPVLAN_MAX +}; + +#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) + +enum ipvlan_mode { + IPVLAN_MODE_L2 = 0, + IPVLAN_MODE_L3, + IPVLAN_MODE_L3S, + IPVLAN_MODE_MAX +}; + +#define IPVLAN_F_PRIVATE 0x01 +#define IPVLAN_F_VEPA 0x02 + +/* Tunnel RTM header */ +struct tunnel_msg { + __u8 family; + __u8 flags; + __u16 reserved2; + __u32 ifindex; +}; + +/* VXLAN section */ + +/* include statistics in the dump */ +#define TUNNEL_MSG_FLAG_STATS 0x01 + +#define TUNNEL_MSG_VALID_USER_FLAGS TUNNEL_MSG_FLAG_STATS + +/* Embedded inside VXLAN_VNIFILTER_ENTRY_STATS */ +enum { + VNIFILTER_ENTRY_STATS_UNSPEC, + VNIFILTER_ENTRY_STATS_RX_BYTES, + VNIFILTER_ENTRY_STATS_RX_PKTS, + VNIFILTER_ENTRY_STATS_RX_DROPS, + VNIFILTER_ENTRY_STATS_RX_ERRORS, + VNIFILTER_ENTRY_STATS_TX_BYTES, + VNIFILTER_ENTRY_STATS_TX_PKTS, + VNIFILTER_ENTRY_STATS_TX_DROPS, + VNIFILTER_ENTRY_STATS_TX_ERRORS, + VNIFILTER_ENTRY_STATS_PAD, + __VNIFILTER_ENTRY_STATS_MAX +}; +#define VNIFILTER_ENTRY_STATS_MAX (__VNIFILTER_ENTRY_STATS_MAX - 1) + +enum { + VXLAN_VNIFILTER_ENTRY_UNSPEC, + VXLAN_VNIFILTER_ENTRY_START, + VXLAN_VNIFILTER_ENTRY_END, + VXLAN_VNIFILTER_ENTRY_GROUP, + VXLAN_VNIFILTER_ENTRY_GROUP6, + VXLAN_VNIFILTER_ENTRY_STATS, + __VXLAN_VNIFILTER_ENTRY_MAX +}; +#define VXLAN_VNIFILTER_ENTRY_MAX (__VXLAN_VNIFILTER_ENTRY_MAX - 1) + +enum { + VXLAN_VNIFILTER_UNSPEC, + VXLAN_VNIFILTER_ENTRY, + __VXLAN_VNIFILTER_MAX +}; +#define VXLAN_VNIFILTER_MAX (__VXLAN_VNIFILTER_MAX - 1) + +enum { + IFLA_VXLAN_UNSPEC, + IFLA_VXLAN_ID, + IFLA_VXLAN_GROUP, /* group or remote address */ + IFLA_VXLAN_LINK, + IFLA_VXLAN_LOCAL, + IFLA_VXLAN_TTL, + IFLA_VXLAN_TOS, + IFLA_VXLAN_LEARNING, + IFLA_VXLAN_AGEING, + IFLA_VXLAN_LIMIT, + IFLA_VXLAN_PORT_RANGE, /* source port */ + IFLA_VXLAN_PROXY, + IFLA_VXLAN_RSC, + IFLA_VXLAN_L2MISS, + IFLA_VXLAN_L3MISS, + IFLA_VXLAN_PORT, /* destination port */ + IFLA_VXLAN_GROUP6, + IFLA_VXLAN_LOCAL6, + IFLA_VXLAN_UDP_CSUM, + IFLA_VXLAN_UDP_ZERO_CSUM6_TX, + IFLA_VXLAN_UDP_ZERO_CSUM6_RX, + IFLA_VXLAN_REMCSUM_TX, + IFLA_VXLAN_REMCSUM_RX, + IFLA_VXLAN_GBP, + IFLA_VXLAN_REMCSUM_NOPARTIAL, + IFLA_VXLAN_COLLECT_METADATA, + IFLA_VXLAN_LABEL, + IFLA_VXLAN_GPE, + IFLA_VXLAN_TTL_INHERIT, + IFLA_VXLAN_DF, + IFLA_VXLAN_VNIFILTER, /* only applicable with COLLECT_METADATA mode */ + IFLA_VXLAN_LOCALBYPASS, + __IFLA_VXLAN_MAX +}; +#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) + +struct ifla_vxlan_port_range { + __be16 low; + __be16 high; +}; + +enum ifla_vxlan_df { + VXLAN_DF_UNSET = 0, + VXLAN_DF_SET, + VXLAN_DF_INHERIT, + __VXLAN_DF_END, + VXLAN_DF_MAX = __VXLAN_DF_END - 1, +}; + +/* GENEVE section */ +enum { + IFLA_GENEVE_UNSPEC, + IFLA_GENEVE_ID, + IFLA_GENEVE_REMOTE, + IFLA_GENEVE_TTL, + IFLA_GENEVE_TOS, + IFLA_GENEVE_PORT, /* destination port */ + IFLA_GENEVE_COLLECT_METADATA, + IFLA_GENEVE_REMOTE6, + IFLA_GENEVE_UDP_CSUM, + IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + IFLA_GENEVE_LABEL, + IFLA_GENEVE_TTL_INHERIT, + IFLA_GENEVE_DF, + IFLA_GENEVE_INNER_PROTO_INHERIT, + __IFLA_GENEVE_MAX +}; +#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) + +enum ifla_geneve_df { + GENEVE_DF_UNSET = 0, + GENEVE_DF_SET, + GENEVE_DF_INHERIT, + __GENEVE_DF_END, + GENEVE_DF_MAX = __GENEVE_DF_END - 1, +}; + +/* Bareudp section */ +enum { + IFLA_BAREUDP_UNSPEC, + IFLA_BAREUDP_PORT, + IFLA_BAREUDP_ETHERTYPE, + IFLA_BAREUDP_SRCPORT_MIN, + IFLA_BAREUDP_MULTIPROTO_MODE, + __IFLA_BAREUDP_MAX +}; + +#define IFLA_BAREUDP_MAX (__IFLA_BAREUDP_MAX - 1) + +/* PPP section */ +enum { + IFLA_PPP_UNSPEC, + IFLA_PPP_DEV_FD, + __IFLA_PPP_MAX +}; +#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1) + +/* GTP section */ + +enum ifla_gtp_role { + GTP_ROLE_GGSN = 0, + GTP_ROLE_SGSN, +}; + +enum { + IFLA_GTP_UNSPEC, + IFLA_GTP_FD0, + IFLA_GTP_FD1, + IFLA_GTP_PDP_HASHSIZE, + IFLA_GTP_ROLE, + IFLA_GTP_CREATE_SOCKETS, + IFLA_GTP_RESTART_COUNT, + __IFLA_GTP_MAX, +}; +#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) + +/* Bonding section */ + +enum { + IFLA_BOND_UNSPEC, + IFLA_BOND_MODE, + IFLA_BOND_ACTIVE_SLAVE, + IFLA_BOND_MIIMON, + IFLA_BOND_UPDELAY, + IFLA_BOND_DOWNDELAY, + IFLA_BOND_USE_CARRIER, + IFLA_BOND_ARP_INTERVAL, + IFLA_BOND_ARP_IP_TARGET, + IFLA_BOND_ARP_VALIDATE, + IFLA_BOND_ARP_ALL_TARGETS, + IFLA_BOND_PRIMARY, + IFLA_BOND_PRIMARY_RESELECT, + IFLA_BOND_FAIL_OVER_MAC, + IFLA_BOND_XMIT_HASH_POLICY, + IFLA_BOND_RESEND_IGMP, + IFLA_BOND_NUM_PEER_NOTIF, + IFLA_BOND_ALL_SLAVES_ACTIVE, + IFLA_BOND_MIN_LINKS, + IFLA_BOND_LP_INTERVAL, + IFLA_BOND_PACKETS_PER_SLAVE, + IFLA_BOND_AD_LACP_RATE, + IFLA_BOND_AD_SELECT, + IFLA_BOND_AD_INFO, + IFLA_BOND_AD_ACTOR_SYS_PRIO, + IFLA_BOND_AD_USER_PORT_KEY, + IFLA_BOND_AD_ACTOR_SYSTEM, + IFLA_BOND_TLB_DYNAMIC_LB, + IFLA_BOND_PEER_NOTIF_DELAY, + IFLA_BOND_AD_LACP_ACTIVE, + IFLA_BOND_MISSED_MAX, + IFLA_BOND_NS_IP6_TARGET, + __IFLA_BOND_MAX, +}; + +#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) + +enum { + IFLA_BOND_AD_INFO_UNSPEC, + IFLA_BOND_AD_INFO_AGGREGATOR, + IFLA_BOND_AD_INFO_NUM_PORTS, + IFLA_BOND_AD_INFO_ACTOR_KEY, + IFLA_BOND_AD_INFO_PARTNER_KEY, + IFLA_BOND_AD_INFO_PARTNER_MAC, + __IFLA_BOND_AD_INFO_MAX, +}; + +#define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1) + +enum { + IFLA_BOND_SLAVE_UNSPEC, + IFLA_BOND_SLAVE_STATE, + IFLA_BOND_SLAVE_MII_STATUS, + IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, + IFLA_BOND_SLAVE_PERM_HWADDR, + IFLA_BOND_SLAVE_QUEUE_ID, + IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, + IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, + IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, + IFLA_BOND_SLAVE_PRIO, + __IFLA_BOND_SLAVE_MAX, +}; + +#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1) + +/* SR-IOV virtual function management section */ + +enum { + IFLA_VF_INFO_UNSPEC, + IFLA_VF_INFO, + __IFLA_VF_INFO_MAX, +}; + +#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1) + +enum { + IFLA_VF_UNSPEC, + IFLA_VF_MAC, /* Hardware queue specific attributes */ + IFLA_VF_VLAN, /* VLAN ID and QoS */ + IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ + IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ + IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ + IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */ + IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query + * on/off switch + */ + IFLA_VF_STATS, /* network device statistics */ + IFLA_VF_TRUST, /* Trust VF */ + IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */ + IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */ + IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */ + IFLA_VF_BROADCAST, /* VF broadcast */ + __IFLA_VF_MAX, +}; + +#define IFLA_VF_MAX (__IFLA_VF_MAX - 1) + +struct ifla_vf_mac { + __u32 vf; + __u8 mac[32]; /* MAX_ADDR_LEN */ +}; + +struct ifla_vf_broadcast { + __u8 broadcast[32]; +}; + +struct ifla_vf_vlan { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; +}; + +enum { + IFLA_VF_VLAN_INFO_UNSPEC, + IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */ + __IFLA_VF_VLAN_INFO_MAX, +}; + +#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1) +#define MAX_VLAN_LIST_LEN 1 + +struct ifla_vf_vlan_info { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; + __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */ +}; + +struct ifla_vf_tx_rate { + __u32 vf; + __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ +}; + +struct ifla_vf_rate { + __u32 vf; + __u32 min_tx_rate; /* Min Bandwidth in Mbps */ + __u32 max_tx_rate; /* Max Bandwidth in Mbps */ +}; + +struct ifla_vf_spoofchk { + __u32 vf; + __u32 setting; +}; + +struct ifla_vf_guid { + __u32 vf; + __u64 guid; +}; + +enum { + IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */ + IFLA_VF_LINK_STATE_ENABLE, /* link always up */ + IFLA_VF_LINK_STATE_DISABLE, /* link always down */ + __IFLA_VF_LINK_STATE_MAX, +}; + +struct ifla_vf_link_state { + __u32 vf; + __u32 link_state; +}; + +struct ifla_vf_rss_query_en { + __u32 vf; + __u32 setting; +}; + +enum { + IFLA_VF_STATS_RX_PACKETS, + IFLA_VF_STATS_TX_PACKETS, + IFLA_VF_STATS_RX_BYTES, + IFLA_VF_STATS_TX_BYTES, + IFLA_VF_STATS_BROADCAST, + IFLA_VF_STATS_MULTICAST, + IFLA_VF_STATS_PAD, + IFLA_VF_STATS_RX_DROPPED, + IFLA_VF_STATS_TX_DROPPED, + __IFLA_VF_STATS_MAX, +}; + +#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1) + +struct ifla_vf_trust { + __u32 vf; + __u32 setting; +}; + +/* VF ports management section + * + * Nested layout of set/get msg is: + * + * [IFLA_NUM_VF] + * [IFLA_VF_PORTS] + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * ... + * [IFLA_PORT_SELF] + * [IFLA_PORT_*], ... + */ + +enum { + IFLA_VF_PORT_UNSPEC, + IFLA_VF_PORT, /* nest */ + __IFLA_VF_PORT_MAX, +}; + +#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1) + +enum { + IFLA_PORT_UNSPEC, + IFLA_PORT_VF, /* __u32 */ + IFLA_PORT_PROFILE, /* string */ + IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */ + IFLA_PORT_INSTANCE_UUID, /* binary UUID */ + IFLA_PORT_HOST_UUID, /* binary UUID */ + IFLA_PORT_REQUEST, /* __u8 */ + IFLA_PORT_RESPONSE, /* __u16, output only */ + __IFLA_PORT_MAX, +}; + +#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1) + +#define PORT_PROFILE_MAX 40 +#define PORT_UUID_MAX 16 +#define PORT_SELF_VF -1 + +enum { + PORT_REQUEST_PREASSOCIATE = 0, + PORT_REQUEST_PREASSOCIATE_RR, + PORT_REQUEST_ASSOCIATE, + PORT_REQUEST_DISASSOCIATE, +}; + +enum { + PORT_VDP_RESPONSE_SUCCESS = 0, + PORT_VDP_RESPONSE_INVALID_FORMAT, + PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_VDP_RESPONSE_UNUSED_VTID, + PORT_VDP_RESPONSE_VTID_VIOLATION, + PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION, + PORT_VDP_RESPONSE_OUT_OF_SYNC, + /* 0x08-0xFF reserved for future VDP use */ + PORT_PROFILE_RESPONSE_SUCCESS = 0x100, + PORT_PROFILE_RESPONSE_INPROGRESS, + PORT_PROFILE_RESPONSE_INVALID, + PORT_PROFILE_RESPONSE_BADSTATE, + PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_PROFILE_RESPONSE_ERROR, +}; + +struct ifla_port_vsi { + __u8 vsi_mgr_id; + __u8 vsi_type_id[3]; + __u8 vsi_type_version; + __u8 pad[3]; +}; + + +/* IPoIB section */ + +enum { + IFLA_IPOIB_UNSPEC, + IFLA_IPOIB_PKEY, + IFLA_IPOIB_MODE, + IFLA_IPOIB_UMCAST, + __IFLA_IPOIB_MAX +}; + +enum { + IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */ + IPOIB_MODE_CONNECTED = 1, /* using connected QPs */ +}; + +#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) + + +/* 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, + IFLA_HSR_SLAVE1, + IFLA_HSR_SLAVE2, + IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */ + 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, +}; + +#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1) + +/* STATS section */ + +struct if_stats_msg { + __u8 family; + __u8 pad1; + __u16 pad2; + __u32 ifindex; + __u32 filter_mask; +}; + +/* A stats attribute can be netdev specific or a global stat. + * For netdev stats, lets use the prefix IFLA_STATS_LINK_* + */ +enum { + IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ + IFLA_STATS_LINK_64, + IFLA_STATS_LINK_XSTATS, + IFLA_STATS_LINK_XSTATS_SLAVE, + IFLA_STATS_LINK_OFFLOAD_XSTATS, + IFLA_STATS_AF_SPEC, + __IFLA_STATS_MAX, +}; + +#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1) + +#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) + +enum { + IFLA_STATS_GETSET_UNSPEC, + IFLA_STATS_GET_FILTERS, /* Nest of IFLA_STATS_LINK_xxx, each a u32 with + * a filter mask for the corresponding group. + */ + IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS, /* 0 or 1 as u8 */ + __IFLA_STATS_GETSET_MAX, +}; + +#define IFLA_STATS_GETSET_MAX (__IFLA_STATS_GETSET_MAX - 1) + +/* These are embedded into IFLA_STATS_LINK_XSTATS: + * [IFLA_STATS_LINK_XSTATS] + * -> [LINK_XSTATS_TYPE_xxx] + * -> [rtnl link type specific attributes] + */ +enum { + LINK_XSTATS_TYPE_UNSPEC, + LINK_XSTATS_TYPE_BRIDGE, + LINK_XSTATS_TYPE_BOND, + __LINK_XSTATS_TYPE_MAX +}; +#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1) + +/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */ +enum { + IFLA_OFFLOAD_XSTATS_UNSPEC, + IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */ + IFLA_OFFLOAD_XSTATS_HW_S_INFO, /* HW stats info. A nest */ + IFLA_OFFLOAD_XSTATS_L3_STATS, /* struct rtnl_hw_stats64 */ + __IFLA_OFFLOAD_XSTATS_MAX +}; +#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1) + +enum { + IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC, + IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST, /* u8 */ + IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED, /* u8 */ + __IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX, +}; +#define IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX \ + (__IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX - 1) + +/* XDP section */ + +#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) +#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_REPLACE) + +/* These are stored into IFLA_XDP_ATTACHED on dump. */ +enum { + XDP_ATTACHED_NONE = 0, + XDP_ATTACHED_DRV, + XDP_ATTACHED_SKB, + XDP_ATTACHED_HW, + XDP_ATTACHED_MULTI, +}; + +enum { + IFLA_XDP_UNSPEC, + IFLA_XDP_FD, + IFLA_XDP_ATTACHED, + IFLA_XDP_FLAGS, + IFLA_XDP_PROG_ID, + IFLA_XDP_DRV_PROG_ID, + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, + __IFLA_XDP_MAX, +}; + +#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) + +enum { + IFLA_EVENT_NONE, + IFLA_EVENT_REBOOT, /* internal reset / reboot */ + IFLA_EVENT_FEATURES, /* change in offload features */ + IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */ + IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */ + IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */ + IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */ +}; + +/* tun section */ + +enum { + IFLA_TUN_UNSPEC, + IFLA_TUN_OWNER, + IFLA_TUN_GROUP, + IFLA_TUN_TYPE, + IFLA_TUN_PI, + IFLA_TUN_VNET_HDR, + IFLA_TUN_PERSIST, + IFLA_TUN_MULTI_QUEUE, + IFLA_TUN_NUM_QUEUES, + IFLA_TUN_NUM_DISABLED_QUEUES, + __IFLA_TUN_MAX, +}; + +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) + +/* rmnet section */ + +#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) +#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 4) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 5) + +enum { + IFLA_RMNET_UNSPEC, + IFLA_RMNET_MUX_ID, + IFLA_RMNET_FLAGS, + __IFLA_RMNET_MAX, +}; + +#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1) + +struct ifla_rmnet_flags { + __u32 flags; + __u32 mask; +}; + +/* MCTP section */ + +enum { + IFLA_MCTP_UNSPEC, + IFLA_MCTP_NET, + __IFLA_MCTP_MAX, +}; + +#define IFLA_MCTP_MAX (__IFLA_MCTP_MAX - 1) + +/* DSA section */ + +enum { + IFLA_DSA_UNSPEC, + IFLA_DSA_MASTER, + __IFLA_DSA_MAX, +}; + +#define IFLA_DSA_MAX (__IFLA_DSA_MAX - 1) + +#endif /* _LINUX_IF_LINK_H */ diff --git a/uapi/linux/libc-compat.h b/uapi/linux/libc-compat.h new file mode 100644 index 0000000..a159991 --- /dev/null +++ b/uapi/linux/libc-compat.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Compatibility interface for userspace libc header coordination: + * + * Define compatibility macros that are used to control the inclusion or + * exclusion of UAPI structures and definitions in coordination with another + * userspace C library. + * + * This header is intended to solve the problem of UAPI definitions that + * conflict with userspace definitions. If a UAPI header has such conflicting + * definitions then the solution is as follows: + * + * * Synchronize the UAPI header and the libc headers so either one can be + * used and such that the ABI is preserved. If this is not possible then + * no simple compatibility interface exists (you need to write translating + * wrappers and rename things) and you can't use this interface. + * + * Then follow this process: + * + * (a) Include libc-compat.h in the UAPI header. + * e.g. #include <linux/libc-compat.h> + * This include must be as early as possible. + * + * (b) In libc-compat.h add enough code to detect that the comflicting + * userspace libc header has been included first. + * + * (c) If the userspace libc header has been included first define a set of + * guard macros of the form __UAPI_DEF_FOO and set their values to 1, else + * set their values to 0. + * + * (d) Back in the UAPI header with the conflicting definitions, guard the + * definitions with: + * #if __UAPI_DEF_FOO + * ... + * #endif + * + * This fixes the situation where the linux headers are included *after* the + * libc headers. To fix the problem with the inclusion in the other order the + * userspace libc headers must be fixed like this: + * + * * For all definitions that conflict with kernel definitions wrap those + * defines in the following: + * #if !__UAPI_DEF_FOO + * ... + * #endif + * + * This prevents the redefinition of a construct already defined by the kernel. + */ +#ifndef _LIBC_COMPAT_H +#define _LIBC_COMPAT_H + +/* We have included glibc headers... */ +#if defined(__GLIBC__) + +/* Coordinate with glibc net/if.h header. */ +#if defined(_NET_IF_H) && defined(__USE_MISC) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ + +#define __UAPI_DEF_IF_IFCONF 0 +#define __UAPI_DEF_IF_IFMAP 0 +#define __UAPI_DEF_IF_IFNAMSIZ 0 +#define __UAPI_DEF_IF_IFREQ 0 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#else /* _NET_IF_H */ + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ + +#define __UAPI_DEF_IF_IFCONF 1 +#define __UAPI_DEF_IF_IFMAP 1 +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#define __UAPI_DEF_IF_IFREQ 1 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 + +#endif /* _NET_IF_H */ + +/* Coordinate with glibc netinet/in.h header. */ +#if defined(_NETINET_IN_H) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ +#define __UAPI_DEF_IN_ADDR 0 +#define __UAPI_DEF_IN_IPPROTO 0 +#define __UAPI_DEF_IN_PKTINFO 0 +#define __UAPI_DEF_IP_MREQ 0 +#define __UAPI_DEF_SOCKADDR_IN 0 +#define __UAPI_DEF_IN_CLASS 0 + +#define __UAPI_DEF_IN6_ADDR 0 +/* The exception is the in6_addr macros which must be defined + * if the glibc code didn't define them. This guard matches + * the guard in glibc/inet/netinet/in.h which defines the + * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */ +#if defined(__USE_MISC) || defined (__USE_GNU) +#define __UAPI_DEF_IN6_ADDR_ALT 0 +#else +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#define __UAPI_DEF_SOCKADDR_IN6 0 +#define __UAPI_DEF_IPV6_MREQ 0 +#define __UAPI_DEF_IPPROTO_V6 0 +#define __UAPI_DEF_IPV6_OPTIONS 0 +#define __UAPI_DEF_IN6_PKTINFO 0 +#define __UAPI_DEF_IP6_MTUINFO 0 + +#else + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ +#define __UAPI_DEF_IN_ADDR 1 +#define __UAPI_DEF_IN_IPPROTO 1 +#define __UAPI_DEF_IN_PKTINFO 1 +#define __UAPI_DEF_IP_MREQ 1 +#define __UAPI_DEF_SOCKADDR_IN 1 +#define __UAPI_DEF_IN_CLASS 1 + +#define __UAPI_DEF_IN6_ADDR 1 +/* We unconditionally define the in6_addr macros and glibc must + * coordinate. */ +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#define __UAPI_DEF_IPV6_MREQ 1 +#define __UAPI_DEF_IPPROTO_V6 1 +#define __UAPI_DEF_IPV6_OPTIONS 1 +#define __UAPI_DEF_IN6_PKTINFO 1 +#define __UAPI_DEF_IP6_MTUINFO 1 + +#endif /* _NETINET_IN_H */ + +/* Coordinate with glibc netipx/ipx.h header. */ +#if defined(__NETIPX_IPX_H) + +#define __UAPI_DEF_SOCKADDR_IPX 0 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0 +#define __UAPI_DEF_IPX_CONFIG_DATA 0 +#define __UAPI_DEF_IPX_ROUTE_DEF 0 + +#else /* defined(__NETIPX_IPX_H) */ + +#define __UAPI_DEF_SOCKADDR_IPX 1 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#define __UAPI_DEF_IPX_ROUTE_DEF 1 + +#endif /* defined(__NETIPX_IPX_H) */ + +/* Definitions for xattr.h */ +#if defined(_SYS_XATTR_H) +#define __UAPI_DEF_XATTR 0 +#else +#define __UAPI_DEF_XATTR 1 +#endif + +/* If we did not see any headers from any supported C libraries, + * or we are being included in the kernel, then define everything + * that we need. Check for previous __UAPI_* definitions to give + * unsupported C libraries a way to opt out of any kernel definition. */ +#else /* !defined(__GLIBC__) */ + +/* Definitions for if.h */ +#ifndef __UAPI_DEF_IF_IFCONF +#define __UAPI_DEF_IF_IFCONF 1 +#endif +#ifndef __UAPI_DEF_IF_IFMAP +#define __UAPI_DEF_IF_IFMAP 1 +#endif +#ifndef __UAPI_DEF_IF_IFNAMSIZ +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#endif +#ifndef __UAPI_DEF_IF_IFREQ +#define __UAPI_DEF_IF_IFREQ 1 +#endif +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +#endif +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif + +/* Definitions for in.h */ +#ifndef __UAPI_DEF_IN_ADDR +#define __UAPI_DEF_IN_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN_IPPROTO +#define __UAPI_DEF_IN_IPPROTO 1 +#endif +#ifndef __UAPI_DEF_IN_PKTINFO +#define __UAPI_DEF_IN_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP_MREQ +#define __UAPI_DEF_IP_MREQ 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN +#define __UAPI_DEF_SOCKADDR_IN 1 +#endif +#ifndef __UAPI_DEF_IN_CLASS +#define __UAPI_DEF_IN_CLASS 1 +#endif + +/* Definitions for in6.h */ +#ifndef __UAPI_DEF_IN6_ADDR +#define __UAPI_DEF_IN6_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN6_ADDR_ALT +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN6 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#endif +#ifndef __UAPI_DEF_IPV6_MREQ +#define __UAPI_DEF_IPV6_MREQ 1 +#endif +#ifndef __UAPI_DEF_IPPROTO_V6 +#define __UAPI_DEF_IPPROTO_V6 1 +#endif +#ifndef __UAPI_DEF_IPV6_OPTIONS +#define __UAPI_DEF_IPV6_OPTIONS 1 +#endif +#ifndef __UAPI_DEF_IN6_PKTINFO +#define __UAPI_DEF_IN6_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP6_MTUINFO +#define __UAPI_DEF_IP6_MTUINFO 1 +#endif + +/* Definitions for ipx.h */ +#ifndef __UAPI_DEF_SOCKADDR_IPX +#define __UAPI_DEF_SOCKADDR_IPX 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_CONFIG_DATA +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEF +#define __UAPI_DEF_IPX_ROUTE_DEF 1 +#endif + +/* Definitions for xattr.h */ +#ifndef __UAPI_DEF_XATTR +#define __UAPI_DEF_XATTR 1 +#endif + +#endif /* __GLIBC__ */ + +#endif /* _LIBC_COMPAT_H */ diff --git a/uapi/linux/neighbour.h b/uapi/linux/neighbour.h new file mode 100644 index 0000000..5e67a7e --- /dev/null +++ b/uapi/linux/neighbour.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NEIGHBOUR_H +#define __LINUX_NEIGHBOUR_H + +#include <linux/types.h> +#include <linux/netlink.h> + +struct ndmsg { + __u8 ndm_family; + __u8 ndm_pad1; + __u16 ndm_pad2; + __s32 ndm_ifindex; + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum { + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + NDA_PROBES, + NDA_VLAN, + NDA_PORT, + NDA_VNI, + NDA_IFINDEX, + NDA_MASTER, + NDA_LINK_NETNSID, + NDA_SRC_VNI, + NDA_PROTOCOL, /* Originator of entry */ + NDA_NH_ID, + NDA_FDB_EXT_ATTRS, + NDA_FLAGS_EXT, + NDA_NDM_STATE_MASK, + NDA_NDM_FLAGS_MASK, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_USE (1 << 0) +#define NTF_SELF (1 << 1) +#define NTF_MASTER (1 << 2) +#define NTF_PROXY (1 << 3) /* == ATF_PUBL */ +#define NTF_EXT_LEARNED (1 << 4) +#define NTF_OFFLOADED (1 << 5) +#define NTF_STICKY (1 << 6) +#define NTF_ROUTER (1 << 7) +/* Extended flags under NDA_FLAGS_EXT: */ +#define NTF_EXT_MANAGED (1 << 0) +#define NTF_EXT_LOCKED (1 << 1) + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + +/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change and make no + * address resolution or NUD. + * + * NUD_PERMANENT also cannot be deleted by garbage collectors. This holds true + * for dynamic entries with NTF_EXT_LEARNED flag as well. However, upon carrier + * down event, NUD_PERMANENT entries are not flushed whereas NTF_EXT_LEARNED + * flagged entries explicitly are (which is also consistent with the routing + * subsystem). + * + * When NTF_EXT_LEARNED is set for a bridge fdb entry the different cache entry + * states don't make sense and thus are ignored. Such entries don't age and + * can roam. + * + * NTF_EXT_MANAGED flagged neigbor entries are managed by the kernel on behalf + * of a user space control plane, and automatically refreshed so that (if + * possible) they remain in NUD_REACHABLE state. + * + * NTF_EXT_LOCKED flagged bridge FDB entries are entries generated by the + * bridge in response to a host trying to communicate via a locked bridge port + * with MAB enabled. Their purpose is to notify user space that a host requires + * authentication. + */ + +struct nda_cacheinfo { + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + +/***************************************************************** + * Neighbour tables specific messages. + * + * To retrieve the neighbour tables send RTM_GETNEIGHTBL with the + * NLM_F_DUMP flag set. Every neighbour table configuration is + * spread over multiple messages to avoid running into message + * size limits on systems with many interfaces. The first message + * in the sequence transports all not device specific data such as + * statistics, configuration, and the default parameter set. + * This message is followed by 0..n messages carrying device + * specific parameter sets. + * Although the ordering should be sufficient, NDTA_NAME can be + * used to identify sequences. The initial message can be identified + * by checking for NDTA_CONFIG. The device specific messages do + * not contain this TLV but have NDTPA_IFINDEX set to the + * corresponding interface index. + * + * To change neighbour table attributes, send RTM_SETNEIGHTBL + * with NDTA_NAME set. Changeable attribute include NDTA_THRESH[1-3], + * NDTA_GC_INTERVAL, and all TLVs in NDTA_PARMS unless marked + * otherwise. Device specific parameter sets can be changed by + * setting NDTPA_IFINDEX to the interface index of the corresponding + * device. + ****/ + +struct ndt_stats { + __u64 ndts_allocs; + __u64 ndts_destroys; + __u64 ndts_hash_grows; + __u64 ndts_res_failed; + __u64 ndts_lookups; + __u64 ndts_hits; + __u64 ndts_rcv_probes_mcast; + __u64 ndts_rcv_probes_ucast; + __u64 ndts_periodic_gc_runs; + __u64 ndts_forced_gc_runs; + __u64 ndts_table_fulls; +}; + +enum { + NDTPA_UNSPEC, + NDTPA_IFINDEX, /* u32, unchangeable */ + NDTPA_REFCNT, /* u32, read-only */ + NDTPA_REACHABLE_TIME, /* u64, read-only, msecs */ + NDTPA_BASE_REACHABLE_TIME, /* u64, msecs */ + NDTPA_RETRANS_TIME, /* u64, msecs */ + NDTPA_GC_STALETIME, /* u64, msecs */ + NDTPA_DELAY_PROBE_TIME, /* u64, msecs */ + NDTPA_QUEUE_LEN, /* u32 */ + NDTPA_APP_PROBES, /* u32 */ + NDTPA_UCAST_PROBES, /* u32 */ + NDTPA_MCAST_PROBES, /* u32 */ + NDTPA_ANYCAST_DELAY, /* u64, msecs */ + NDTPA_PROXY_DELAY, /* u64, msecs */ + NDTPA_PROXY_QLEN, /* u32 */ + NDTPA_LOCKTIME, /* u64, msecs */ + NDTPA_QUEUE_LENBYTES, /* u32 */ + NDTPA_MCAST_REPROBES, /* u32 */ + NDTPA_PAD, + NDTPA_INTERVAL_PROBE_TIME_MS, /* u64, msecs */ + __NDTPA_MAX +}; +#define NDTPA_MAX (__NDTPA_MAX - 1) + +struct ndtmsg { + __u8 ndtm_family; + __u8 ndtm_pad1; + __u16 ndtm_pad2; +}; + +struct ndt_config { + __u16 ndtc_key_len; + __u16 ndtc_entry_size; + __u32 ndtc_entries; + __u32 ndtc_last_flush; /* delta to now in msecs */ + __u32 ndtc_last_rand; /* delta to now in msecs */ + __u32 ndtc_hash_rnd; + __u32 ndtc_hash_mask; + __u32 ndtc_hash_chain_gc; + __u32 ndtc_proxy_qlen; +}; + +enum { + NDTA_UNSPEC, + NDTA_NAME, /* char *, unchangeable */ + NDTA_THRESH1, /* u32 */ + NDTA_THRESH2, /* u32 */ + NDTA_THRESH3, /* u32 */ + NDTA_CONFIG, /* struct ndt_config, read-only */ + NDTA_PARMS, /* nested TLV NDTPA_* */ + NDTA_STATS, /* struct ndt_stats, read-only */ + NDTA_GC_INTERVAL, /* u64, msecs */ + NDTA_PAD, + __NDTA_MAX +}; +#define NDTA_MAX (__NDTA_MAX - 1) + + /* FDB activity notification bits used in NFEA_ACTIVITY_NOTIFY: + * - FDB_NOTIFY_BIT - notify on activity/expire for any entry + * - FDB_NOTIFY_INACTIVE_BIT - mark as inactive to avoid multiple notifications + */ +enum { + FDB_NOTIFY_BIT = (1 << 0), + FDB_NOTIFY_INACTIVE_BIT = (1 << 1) +}; + +/* embedded into NDA_FDB_EXT_ATTRS: + * [NDA_FDB_EXT_ATTRS] = { + * [NFEA_ACTIVITY_NOTIFY] + * ... + * } + */ +enum { + NFEA_UNSPEC, + NFEA_ACTIVITY_NOTIFY, + NFEA_DONT_REFRESH, + __NFEA_MAX +}; +#define NFEA_MAX (__NFEA_MAX - 1) + +#endif diff --git a/net_tstamp-copy.h b/uapi/linux/net_tstamp.h index 3d421d9..a2c66b3 100644 --- a/net_tstamp-copy.h +++ b/uapi/linux/net_tstamp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * Userspace API for hardware time stamping of network packets * @@ -12,7 +13,7 @@ #include <linux/types.h> #include <linux/socket.h> /* for SO_TIMESTAMPING */ -/* SO_TIMESTAMPING gets an integer bit field comprised of these values */ +/* SO_TIMESTAMPING flags */ enum { SOF_TIMESTAMPING_TX_HARDWARE = (1<<0), SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1), @@ -29,8 +30,10 @@ enum { SOF_TIMESTAMPING_OPT_STATS = (1<<12), SOF_TIMESTAMPING_OPT_PKTINFO = (1<<13), SOF_TIMESTAMPING_OPT_TX_SWHW = (1<<14), + SOF_TIMESTAMPING_BIND_PHC = (1 << 15), + SOF_TIMESTAMPING_OPT_ID_TCP = (1 << 16), - SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_TX_SWHW, + SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_ID_TCP, SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) | SOF_TIMESTAMPING_LAST }; @@ -46,9 +49,21 @@ enum { SOF_TIMESTAMPING_TX_ACK) /** + * struct so_timestamping - SO_TIMESTAMPING parameter + * + * @flags: SO_TIMESTAMPING flags + * @bind_phc: Index of PTP virtual clock bound to sock. This is available + * if flag SOF_TIMESTAMPING_BIND_PHC is set. + */ +struct so_timestamping { + int flags; + int bind_phc; +}; + +/** * struct hwtstamp_config - %SIOCGHWTSTAMP and %SIOCSHWTSTAMP parameter * - * @flags: no flags defined right now, must be zero for %SIOCSHWTSTAMP + * @flags: one of HWTSTAMP_FLAG_* * @tx_type: one of HWTSTAMP_TX_* * @rx_filter: one of HWTSTAMP_FILTER_* * @@ -64,6 +79,21 @@ struct hwtstamp_config { int rx_filter; }; +/* possible values for hwtstamp_config->flags */ +enum hwtstamp_flags { + /* + * With this flag, the user could get bond active interface's + * PHC index. Note this PHC index is not stable as when there + * is a failover, the bond active interface will be changed, so + * will be the PHC index. + */ + HWTSTAMP_FLAG_BONDED_PHC_INDEX = (1<<0), +#define HWTSTAMP_FLAG_BONDED_PHC_INDEX HWTSTAMP_FLAG_BONDED_PHC_INDEX + + HWTSTAMP_FLAG_LAST = HWTSTAMP_FLAG_BONDED_PHC_INDEX, + HWTSTAMP_FLAG_MASK = (HWTSTAMP_FLAG_LAST - 1) | HWTSTAMP_FLAG_LAST +}; + /* possible values for hwtstamp_config->tx_type */ enum hwtstamp_tx_types { /* @@ -89,6 +119,17 @@ enum hwtstamp_tx_types { * queue. */ HWTSTAMP_TX_ONESTEP_SYNC, + + /* + * Same as HWTSTAMP_TX_ONESTEP_SYNC, but also enables time + * stamp insertion directly into PDelay_Resp packets. In this + * case, neither transmitted Sync nor PDelay_Resp packets will + * 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 */ @@ -131,6 +172,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 */ @@ -140,4 +184,22 @@ struct scm_ts_pktinfo { __u32 reserved[2]; }; +/* + * SO_TXTIME gets a struct sock_txtime with flags being an integer bit + * field comprised of these values. + */ +enum txtime_flags { + SOF_TXTIME_DEADLINE_MODE = (1 << 0), + SOF_TXTIME_REPORT_ERRORS = (1 << 1), + + SOF_TXTIME_FLAGS_LAST = SOF_TXTIME_REPORT_ERRORS, + SOF_TXTIME_FLAGS_MASK = (SOF_TXTIME_FLAGS_LAST - 1) | + SOF_TXTIME_FLAGS_LAST +}; + +struct sock_txtime { + __kernel_clockid_t clockid;/* reference clockid */ + __u32 flags; /* as defined by enum txtime_flags */ +}; + #endif /* _NET_TIMESTAMPING_H */ diff --git a/uapi/linux/netlink.h b/uapi/linux/netlink.h new file mode 100644 index 0000000..47bac97 --- /dev/null +++ b/uapi/linux/netlink.h @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include <linux/const.h> +#include <linux/socket.h> /* for __kernel_sa_family_t */ +#include <linux/types.h> + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ +#define NETLINK_SOCK_DIAG 4 /* socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages (obsolete) */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_GENERIC 16 +/* leave room for NETLINK_DM (DM Events) */ +#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ +#define NETLINK_ECRYPTFS 19 +#define NETLINK_RDMA 20 +#define NETLINK_CRYPTO 21 /* Crypto layer */ +#define NETLINK_SMC 22 /* SMC monitoring */ + +#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG + +#define MAX_LINKS 32 + +struct sockaddr_nl { + __kernel_sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* port ID */ + __u32 nl_groups; /* multicast groups mask */ +}; + +/** + * struct nlmsghdr - fixed format metadata header of Netlink messages + * @nlmsg_len: Length of message including header + * @nlmsg_type: Message content type + * @nlmsg_flags: Additional flags + * @nlmsg_seq: Sequence number + * @nlmsg_pid: Sending process port ID + */ +struct nlmsghdr { + __u32 nlmsg_len; + __u16 nlmsg_type; + __u16 nlmsg_flags; + __u32 nlmsg_seq; + __u32 nlmsg_pid; +}; + +/* Flags values */ + +#define NLM_F_REQUEST 0x01 /* It is request message. */ +#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 0x08 /* Receive resulting notifications */ +#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */ +#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* Modifiers to DELETE request */ +#define NLM_F_NONREC 0x100 /* Do not delete recursively */ +#define NLM_F_BULK 0x200 /* Delete multiple objects */ + +/* Flags for ACK message */ +#define NLM_F_CAPPED 0x100 /* request was capped */ +#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4U +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void *)(((char *)nlh) + NLMSG_HDRLEN)) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr *)(((char *)(nlh)) + \ + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ + +struct nlmsgerr { + int error; + struct nlmsghdr msg; + /* + * followed by the message contents unless NETLINK_CAP_ACK was set + * or the ACK indicates success (error == 0) + * message length is aligned with NLMSG_ALIGN() + */ + /* + * followed by TLVs defined in enum nlmsgerr_attrs + * if NETLINK_EXT_ACK was set + */ +}; + +/** + * enum nlmsgerr_attrs - nlmsgerr attributes + * @NLMSGERR_ATTR_UNUSED: unused + * @NLMSGERR_ATTR_MSG: error message string (string) + * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original + * message, counting from the beginning of the header (u32) + * @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_MISS_TYPE: type of a missing required attribute, + * %NLMSGERR_ATTR_MISS_NEST will not be present if the attribute was + * missing at the message level + * @NLMSGERR_ATTR_MISS_NEST: offset of the nest where attribute was missing + * @__NLMSGERR_ATTR_MAX: number of attributes + * @NLMSGERR_ATTR_MAX: highest attribute number + */ +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + NLMSGERR_ATTR_POLICY, + NLMSGERR_ATTR_MISS_TYPE, + NLMSGERR_ATTR_MISS_NEST, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 +#define NETLINK_RX_RING 6 +#define NETLINK_TX_RING 7 +#define NETLINK_LISTEN_ALL_NSID 8 +#define NETLINK_LIST_MEMBERSHIPS 9 +#define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 +#define NETLINK_GET_STRICT_CHK 12 + +struct nl_pktinfo { + __u32 group; +}; + +struct nl_mmap_req { + unsigned int nm_block_size; + unsigned int nm_block_nr; + unsigned int nm_frame_size; + unsigned int nm_frame_nr; +}; + +struct nl_mmap_hdr { + unsigned int nm_status; + unsigned int nm_len; + __u32 nm_group; + /* credentials */ + __u32 nm_pid; + __u32 nm_uid; + __u32 nm_gid; +}; + +enum nl_mmap_status { + NL_MMAP_STATUS_UNUSED, + NL_MMAP_STATUS_RESERVED, + NL_MMAP_STATUS_VALID, + NL_MMAP_STATUS_COPY, + NL_MMAP_STATUS_SKIP, +}; + +#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO +#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) +#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr { + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +/* Generic 32 bitflags attribute content sent to the kernel. + * + * The value is a bitmap that defines the values being set + * The selector is a bitmask that defines which value is legit + * + * Examples: + * value = 0x0, and selector = 0x1 + * implies we are selecting bit 1 and we want to set its value to 0. + * + * value = 0x2, and selector = 0x2 + * implies we are selecting bit 2 and we want to set its value to 1. + * + */ +struct nla_bitfield32 { + __u32 value; + __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 + * + * @__NL_POLICY_TYPE_ATTR_MAX: number of attributes + * @NL_POLICY_TYPE_ATTR_MAX: highest attribute number + */ +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/posix_types.h b/uapi/linux/posix_types.h new file mode 100644 index 0000000..9a7a740 --- /dev/null +++ b/uapi/linux/posix_types.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_POSIX_TYPES_H +#define _LINUX_POSIX_TYPES_H + +#include <linux/stddef.h> + +/* + * This allows for 1024 file descriptors: if NR_OPEN is ever grown + * beyond that you'll have to change this too. But 1024 fd's seem to be + * enough even for such "real" unices like OSF/1, so hopefully this is + * one limit that doesn't have to be changed [again]. + * + * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in + * <sys/time.h> (and thus <linux/time.h>) - but this is a more logical + * place for them. Solved by having dummy defines in <sys/time.h>. + */ + +/* + * This macro may have been defined in <gnu/types.h>. But we always + * use the one here. + */ +#undef __FD_SETSIZE +#define __FD_SETSIZE 1024 + +typedef struct { + unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))]; +} __kernel_fd_set; + +/* Type of a signal handler. */ +typedef void (*__kernel_sighandler_t)(int); + +/* Type of a SYSV IPC key. */ +typedef int __kernel_key_t; +typedef int __kernel_mqd_t; + +#include <asm/posix_types.h> + +#endif /* _LINUX_POSIX_TYPES_H */ diff --git a/uapi/linux/rtnetlink.h b/uapi/linux/rtnetlink.h new file mode 100644 index 0000000..2132e94 --- /dev/null +++ b/uapi/linux/rtnetlink.h @@ -0,0 +1,826 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include <linux/types.h> +#include <linux/netlink.h> +#include <linux/if_link.h> +#include <linux/if_addr.h> +#include <linux/neighbour.h> + +/* rtnetlink families. Values up to 127 are reserved for real address + * families, values above 128 may be used arbitrarily. + */ +#define RTNL_FAMILY_IPMR 128 +#define RTNL_FAMILY_IP6MR 129 +#define RTNL_FAMILY_MAX 129 + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_NEWNEIGHTBL = 64, +#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL + RTM_GETNEIGHTBL = 66, +#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL + RTM_SETNEIGHTBL, +#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + + RTM_NEWNDUSEROPT = 68, +#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT + + RTM_NEWADDRLABEL = 72, +#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL + RTM_DELADDRLABEL, +#define RTM_DELADDRLABEL RTM_DELADDRLABEL + RTM_GETADDRLABEL, +#define RTM_GETADDRLABEL RTM_GETADDRLABEL + + RTM_GETDCB = 78, +#define RTM_GETDCB RTM_GETDCB + RTM_SETDCB, +#define RTM_SETDCB RTM_SETDCB + + RTM_NEWNETCONF = 80, +#define RTM_NEWNETCONF RTM_NEWNETCONF + RTM_DELNETCONF, +#define RTM_DELNETCONF RTM_DELNETCONF + RTM_GETNETCONF = 82, +#define RTM_GETNETCONF RTM_GETNETCONF + + RTM_NEWMDB = 84, +#define RTM_NEWMDB RTM_NEWMDB + RTM_DELMDB = 85, +#define RTM_DELMDB RTM_DELMDB + RTM_GETMDB = 86, +#define RTM_GETMDB RTM_GETMDB + + RTM_NEWNSID = 88, +#define RTM_NEWNSID RTM_NEWNSID + RTM_DELNSID = 89, +#define RTM_DELNSID RTM_DELNSID + RTM_GETNSID = 90, +#define RTM_GETNSID RTM_GETNSID + + RTM_NEWSTATS = 92, +#define RTM_NEWSTATS RTM_NEWSTATS + RTM_GETSTATS = 94, +#define RTM_GETSTATS RTM_GETSTATS + RTM_SETSTATS, +#define RTM_SETSTATS RTM_SETSTATS + + RTM_NEWCACHEREPORT = 96, +#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT + + RTM_NEWCHAIN = 100, +#define RTM_NEWCHAIN RTM_NEWCHAIN + RTM_DELCHAIN, +#define RTM_DELCHAIN RTM_DELCHAIN + RTM_GETCHAIN, +#define RTM_GETCHAIN RTM_GETCHAIN + + RTM_NEWNEXTHOP = 104, +#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP + RTM_DELNEXTHOP, +#define RTM_DELNEXTHOP RTM_DELNEXTHOP + RTM_GETNEXTHOP, +#define RTM_GETNEXTHOP RTM_GETNEXTHOP + + RTM_NEWLINKPROP = 108, +#define RTM_NEWLINKPROP RTM_NEWLINKPROP + RTM_DELLINKPROP, +#define RTM_DELLINKPROP RTM_DELLINKPROP + RTM_GETLINKPROP, +#define RTM_GETLINKPROP RTM_GETLINKPROP + + RTM_NEWVLAN = 112, +#define RTM_NEWNVLAN RTM_NEWVLAN + RTM_DELVLAN, +#define RTM_DELVLAN RTM_DELVLAN + RTM_GETVLAN, +#define RTM_GETVLAN RTM_GETVLAN + + RTM_NEWNEXTHOPBUCKET = 116, +#define RTM_NEWNEXTHOPBUCKET RTM_NEWNEXTHOPBUCKET + RTM_DELNEXTHOPBUCKET, +#define RTM_DELNEXTHOPBUCKET RTM_DELNEXTHOPBUCKET + RTM_GETNEXTHOPBUCKET, +#define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET + + RTM_NEWTUNNEL = 120, +#define RTM_NEWTUNNEL RTM_NEWTUNNEL + RTM_DELTUNNEL, +#define RTM_DELTUNNEL RTM_DELTUNNEL + RTM_GETTUNNEL, +#define RTM_GETTUNNEL RTM_GETTUNNEL + + __RTM_MAX, +#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) +}; + +#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) +#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) +#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr { + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4U +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg { + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum { + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* 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 */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + 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_KEEPALIVED 18 /* Keepalived daemon */ +#define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_OPENR 99 /* Open Routing (Open/R) Routes */ +#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 + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t { + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ +#define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */ +#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */ +#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */ +#define RTM_F_TRAP 0x8000 /* route is trapping packets */ +#define RTM_F_OFFLOAD_FAILED 0x20000000 /* route offload failed, this value + * is chosen to avoid conflicts with + * other flags defined in + * include/uapi/linux/ipv6_route.h + */ + +/* Reserved table identifiers */ + +enum rt_class_t { + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_COMPAT=252, + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + RT_TABLE_MAX=0xFFFFFFFF +}; + + +/* Routing message attributes */ + +enum rtattr_type_t { + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, /* no longer used */ + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, /* no longer used */ + RTA_MP_ALGO, /* no longer used */ + RTA_TABLE, + RTA_MARK, + RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, + RTA_PREF, + RTA_ENCAP_TYPE, + RTA_ENCAP, + RTA_EXPIRES, + RTA_PAD, + RTA_UID, + RTA_TTL_PROPAGATE, + RTA_IP_PROTO, + RTA_SPORT, + RTA_DPORT, + RTA_NH_ID, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop { + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ +#define RTNH_F_OFFLOAD 8 /* Nexthop is offloaded */ +#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ +#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */ +#define RTNH_F_TRAP 64 /* Nexthop is trapping packets */ + +#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \ + RTNH_F_OFFLOAD | RTNH_F_TRAP) + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTA_VIA */ +struct rtvia { + __kernel_sa_family_t rtvia_family; + __u8 rtvia_addr[]; +}; + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo { + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum { + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + RTAX_RTO_MIN, +#define RTAX_RTO_MIN RTAX_RTO_MIN + RTAX_INITRWND, +#define RTAX_INITRWND RTAX_INITRWND + RTAX_QUICKACK, +#define RTAX_QUICKACK RTAX_QUICKACK + RTAX_CC_ALGO, +#define RTAX_CC_ALGO RTAX_CC_ALGO + RTAX_FASTOPEN_NO_COOKIE, +#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN (1 << 0) +#define RTAX_FEATURE_SACK (1 << 1) +#define RTAX_FEATURE_TIMESTAMP (1 << 2) +#define RTAX_FEATURE_ALLFRAG (1 << 3) + +#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | \ + RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG) + +struct rta_session { + __u8 proto; + __u8 pad1; + __u16 pad2; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + +struct rta_mfc_stats { + __u64 mfcs_packets; + __u64 mfcs_bytes; + __u64 mfcs_wrong_if; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg { + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg { + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg { + unsigned char prefix_family; + unsigned char prefix_pad1; + unsigned short prefix_pad2; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; + unsigned char prefix_pad3; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo { + __u32 preferred_time; + __u32 valid_time; +}; + + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg { + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; +/* tcm_block_index is used instead of tcm_parent + * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK + */ +#define tcm_block_index tcm_parent + __u32 tcm_info; +}; + +/* For manipulation of filters in shared block, tcm_ifindex is set to + * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index + * which is the block index. + */ +#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU) + +enum { + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_STAB, + TCA_PAD, + TCA_DUMP_INVISIBLE, + TCA_CHAIN, + TCA_HW_OFFLOAD, + TCA_INGRESS_BLOCK, + TCA_EGRESS_BLOCK, + TCA_DUMP_FLAGS, + TCA_EXT_WARN_MSG, + __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)) + +/******************************************************************** + * Neighbor Discovery userland options + ****/ + +struct nduseroptmsg { + unsigned char nduseropt_family; + unsigned char nduseropt_pad1; + unsigned short nduseropt_opts_len; /* Total length of options */ + int nduseropt_ifindex; + __u8 nduseropt_icmp_type; + __u8 nduseropt_icmp_code; + unsigned short nduseropt_pad2; + unsigned int nduseropt_pad3; + /* Followed by one or more ND options */ +}; + +enum { + NDUSEROPT_UNSPEC, + NDUSEROPT_SRCADDR, + __NDUSEROPT_MAX +}; + +#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) + +/* RTnetlink multicast groups - backwards compatibility for userspace */ +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 + +/* RTnetlink multicast groups */ +enum rtnetlink_groups { + RTNLGRP_NONE, +#define RTNLGRP_NONE RTNLGRP_NONE + RTNLGRP_LINK, +#define RTNLGRP_LINK RTNLGRP_LINK + RTNLGRP_NOTIFY, +#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY + RTNLGRP_NEIGH, +#define RTNLGRP_NEIGH RTNLGRP_NEIGH + RTNLGRP_TC, +#define RTNLGRP_TC RTNLGRP_TC + RTNLGRP_IPV4_IFADDR, +#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE, +#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE, +#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR, +#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE, +#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE, +#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO, +#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO + RTNLGRP_DECnet_IFADDR, +#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR + RTNLGRP_NOP2, + RTNLGRP_DECnet_ROUTE, +#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE + RTNLGRP_DECnet_RULE, +#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE + RTNLGRP_NOP4, + RTNLGRP_IPV6_PREFIX, +#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE, +#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE + RTNLGRP_ND_USEROPT, +#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT + RTNLGRP_PHONET_IFADDR, +#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR + RTNLGRP_PHONET_ROUTE, +#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE + RTNLGRP_DCB, +#define RTNLGRP_DCB RTNLGRP_DCB + RTNLGRP_IPV4_NETCONF, +#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF + RTNLGRP_IPV6_NETCONF, +#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF + RTNLGRP_MDB, +#define RTNLGRP_MDB RTNLGRP_MDB + RTNLGRP_MPLS_ROUTE, +#define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE + RTNLGRP_NSID, +#define RTNLGRP_NSID RTNLGRP_NSID + RTNLGRP_MPLS_NETCONF, +#define RTNLGRP_MPLS_NETCONF RTNLGRP_MPLS_NETCONF + RTNLGRP_IPV4_MROUTE_R, +#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R + RTNLGRP_IPV6_MROUTE_R, +#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R + RTNLGRP_NEXTHOP, +#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP + RTNLGRP_BRVLAN, +#define RTNLGRP_BRVLAN RTNLGRP_BRVLAN + RTNLGRP_MCTP_IFADDR, +#define RTNLGRP_MCTP_IFADDR RTNLGRP_MCTP_IFADDR + RTNLGRP_TUNNEL, +#define RTNLGRP_TUNNEL RTNLGRP_TUNNEL + RTNLGRP_STATS, +#define RTNLGRP_STATS RTNLGRP_STATS + __RTNLGRP_MAX +}; +#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) + +/* TC action piece */ +struct tcamsg { + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; + +enum { + TCA_ROOT_UNSPEC, + TCA_ROOT_TAB, +#define TCA_ACT_TAB TCA_ROOT_TAB +#define TCAA_MAX TCA_ROOT_TAB + TCA_ROOT_FLAGS, + TCA_ROOT_COUNT, + TCA_ROOT_TIME_DELTA, /* in msecs */ + TCA_ROOT_EXT_WARN_MSG, + __TCA_ROOT_MAX, +#define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) +}; + +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +/* tcamsg flags stored in attribute TCA_ROOT_FLAGS + * + * TCA_ACT_FLAG_LARGE_DUMP_ON user->kernel to request for larger than + * TCA_ACT_MAX_PRIO actions in a dump. All dump responses will contain the + * number of actions being dumped stored in for user app's consumption in + * TCA_ROOT_COUNT + * + * TCA_ACT_FLAG_TERSE_DUMP user->kernel to request terse (brief) dump that only + * includes essential action info (kind, index, etc.) + * + */ +#define TCA_FLAG_LARGE_DUMP_ON (1 << 0) +#define TCA_ACT_FLAG_LARGE_DUMP_ON TCA_FLAG_LARGE_DUMP_ON +#define TCA_ACT_FLAG_TERSE_DUMP (1 << 1) + +/* New extended info filters for IFLA_EXT_MASK */ +#define RTEXT_FILTER_VF (1 << 0) +#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) +#define RTEXT_FILTER_CFM_CONFIG (1 << 5) +#define RTEXT_FILTER_CFM_STATUS (1 << 6) +#define RTEXT_FILTER_MST (1 << 7) + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/uapi/linux/socket.h b/uapi/linux/socket.h new file mode 100644 index 0000000..89c227f --- /dev/null +++ b/uapi/linux/socket.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SOCKET_H +#define _LINUX_SOCKET_H + +/* + * Desired design of maximum size and alignment (see RFC2553) + */ +#define _K_SS_MAXSIZE 128 /* Implementation specific max size */ + +typedef unsigned short __kernel_sa_family_t; + +/* + * The definition uses anonymous union and struct in order to control the + * default alignment. + */ +struct __kernel_sockaddr_storage { + union { + struct { + __kernel_sa_family_t ss_family; /* address family */ + /* Following field(s) are implementation specific */ + char __data[_K_SS_MAXSIZE - sizeof(unsigned short)]; + /* space to achieve desired size, */ + /* _SS_MAXSIZE value minus size of ss_family */ + }; + void *__align; /* implementation specific desired alignment */ + }; +}; + +#define SOCK_SNDBUF_LOCK 1 +#define SOCK_RCVBUF_LOCK 2 + +#define SOCK_BUF_LOCK_MASK (SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK) + +#define SOCK_TXREHASH_DEFAULT 255 +#define SOCK_TXREHASH_DISABLED 0 +#define SOCK_TXREHASH_ENABLED 1 + +#endif /* _LINUX_SOCKET_H */ diff --git a/uapi/linux/stddef.h b/uapi/linux/stddef.h new file mode 100644 index 0000000..bb6ea51 --- /dev/null +++ b/uapi/linux/stddef.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_STDDEF_H +#define _LINUX_STDDEF_H + + + +#ifndef __always_inline +#define __always_inline __inline__ +#endif + +/** + * __struct_group() - Create a mirrored named and anonyomous struct + * + * @TAG: The tag name for the named sub-struct (usually empty) + * @NAME: The identifier name of the mirrored sub-struct + * @ATTRS: Any struct attributes (usually empty) + * @MEMBERS: The member declarations for the mirrored structs + * + * Used to create an anonymous union of two structs with identical layout + * and size: one anonymous and one named. The former's members can be used + * normally without sub-struct naming, and the latter can be used to + * reason about the start, end, and size of the group of struct members. + * The named struct can also be explicitly tagged for layer reuse, as well + * as both having struct attributes appended. + */ +#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \ + union { \ + struct { MEMBERS } ATTRS; \ + struct TAG { MEMBERS } ATTRS NAME; \ + } + +/** + * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union + * + * @TYPE: The type of each flexible array element + * @NAME: The name of the flexible array member + * + * In order to have a flexible array member in a union or alone in a + * struct, it needs to be wrapped in an anonymous struct with at least 1 + * named member, but that member can be empty. + */ +#define __DECLARE_FLEX_ARRAY(TYPE, NAME) \ + struct { \ + struct { } __empty_ ## NAME; \ + TYPE NAME[]; \ + } +#endif diff --git a/uapi/linux/types.h b/uapi/linux/types.h new file mode 100644 index 0000000..e670013 --- /dev/null +++ b/uapi/linux/types.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_TYPES_H +#define _LINUX_TYPES_H + +#include <asm/types.h> + +#ifndef __ASSEMBLY__ + +#include <linux/posix_types.h> + +#ifdef __SIZEOF_INT128__ +typedef __signed__ __int128 __s128 __attribute__((aligned(16))); +typedef unsigned __int128 __u128 __attribute__((aligned(16))); +#endif + +/* + * Below are truly Linux-specific types that should never collide with + * any application/library that wants linux/types.h. + */ + +/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */ +#ifdef __CHECKER__ +#define __bitwise __attribute__((bitwise)) +#else +#define __bitwise +#endif + +/* The kernel doesn't use this legacy form, but user space does */ +#define __bitwise__ __bitwise + +typedef __u16 __bitwise __le16; +typedef __u16 __bitwise __be16; +typedef __u32 __bitwise __le32; +typedef __u32 __bitwise __be32; +typedef __u64 __bitwise __le64; +typedef __u64 __bitwise __be64; + +typedef __u16 __bitwise __sum16; +typedef __u32 __bitwise __wsum; + +/* + * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid + * common 32/64-bit compat problems. + * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other + * architectures) and to 8-byte boundaries on 64-bit architectures. The new + * aligned_64 type enforces 8-byte alignment so that structs containing + * aligned_64 values have the same alignment on 32-bit and 64-bit architectures. + * No conversions are necessary between 32-bit user-space and a 64-bit kernel. + */ +#define __aligned_u64 __u64 __attribute__((aligned(8))) +#define __aligned_be64 __be64 __attribute__((aligned(8))) +#define __aligned_le64 __le64 __attribute__((aligned(8))) + +typedef unsigned __bitwise __poll_t; + +#endif /* __ASSEMBLY__ */ +#endif /* _LINUX_TYPES_H */ @@ -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; @@ -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; |