aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-15 09:21:25 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-15 09:21:25 +0000
commit5460df5c7c35d1ff278150d830c7b9875663cf9d (patch)
treeef508d3284c713a6eb7ffc2e833f537cb8537b7b
parentc84d8b937dd22fcb3b7ede1f1c1b971edd05e0de (diff)
parent2cab479e280b8349d3a193a980c68400acb18733 (diff)
downloadethtool-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
-rw-r--r--.gitignore2
-rw-r--r--Android.bp60
-rw-r--r--LICENSE6
-rw-r--r--Makefile.am50
-rw-r--r--NEWS231
-rw-r--r--OWNERS2
-rw-r--r--amd8111e.c2
-rw-r--r--at76c50x-usb.c2
-rw-r--r--bnxt.c97
-rw-r--r--cmis.c1041
-rw-r--r--cmis.h252
-rw-r--r--common.c211
-rw-r--r--common.h49
-rw-r--r--configure.ac36
-rw-r--r--cpsw.c193
-rw-r--r--de2104x.c4
-rw-r--r--dsa.c204
-rw-r--r--e100.c2
-rw-r--r--e1000.c2
-rw-r--r--et131x.c2
-rw-r--r--ethtool.8.in554
-rw-r--r--ethtool.c1966
-rw-r--r--ethtool.spec.in1
-rw-r--r--fec.c4
-rw-r--r--fec_8xx.c2
-rw-r--r--fjes.c2
-rw-r--r--fsl_enetc.c259
-rw-r--r--hns3.c829
-rw-r--r--ibm_emac.c4
-rw-r--r--igb.c2
-rw-r--r--igc.c284
-rw-r--r--internal.h84
-rw-r--r--ixgb.c2
-rw-r--r--ixgbe.c2
-rw-r--r--ixgbevf.c2
-rw-r--r--json_print.c228
-rw-r--r--json_print.h67
-rw-r--r--json_writer.c391
-rw-r--r--json_writer.h76
-rw-r--r--lan743x.c73
-rw-r--r--lan78xx.c2
-rw-r--r--libmnl/.gitignore20
-rw-r--r--libmnl/COPYING502
-rw-r--r--libmnl/Make_global.am24
-rw-r--r--libmnl/Makefile.am11
-rw-r--r--libmnl/README28
-rwxr-xr-xlibmnl/autogen.sh4
-rw-r--r--libmnl/config.h1
-rw-r--r--libmnl/configure.ac70
-rw-r--r--libmnl/doxygen/.gitignore4
-rw-r--r--libmnl/doxygen/Makefile.am25
-rw-r--r--libmnl/doxygen/doxygen.cfg.in23
-rw-r--r--libmnl/doxygen/finalize_manpages.sh40
-rw-r--r--libmnl/examples/Makefile.am1
-rw-r--r--libmnl/examples/genl/.gitignore2
-rw-r--r--libmnl/examples/genl/Makefile.am10
-rw-r--r--libmnl/examples/genl/genl-family-get.c241
-rw-r--r--libmnl/examples/genl/genl-group-events.c63
-rw-r--r--libmnl/examples/kobject/.gitignore1
-rw-r--r--libmnl/examples/kobject/Makefile.am6
-rw-r--r--libmnl/examples/kobject/kobject-event.c49
-rw-r--r--libmnl/examples/netfilter/.gitignore6
-rw-r--r--libmnl/examples/netfilter/Makefile.am26
-rw-r--r--libmnl/examples/netfilter/nf-log.c219
-rw-r--r--libmnl/examples/netfilter/nf-queue.c243
-rw-r--r--libmnl/examples/netfilter/nfct-create-batch.c187
-rw-r--r--libmnl/examples/netfilter/nfct-daemon.c365
-rw-r--r--libmnl/examples/netfilter/nfct-dump.c319
-rw-r--r--libmnl/examples/netfilter/nfct-event.c240
-rw-r--r--libmnl/examples/rtnl/.gitignore12
-rw-r--r--libmnl/examples/rtnl/Makefile.am48
-rw-r--r--libmnl/examples/rtnl/rtnl-addr-add.c119
-rw-r--r--libmnl/examples/rtnl/rtnl-addr-dump.c133
-rw-r--r--libmnl/examples/rtnl/rtnl-link-can.c452
-rw-r--r--libmnl/examples/rtnl/rtnl-link-dump.c130
-rw-r--r--libmnl/examples/rtnl/rtnl-link-dump2.c103
-rw-r--r--libmnl/examples/rtnl/rtnl-link-dump3.c103
-rw-r--r--libmnl/examples/rtnl/rtnl-link-event.c95
-rw-r--r--libmnl/examples/rtnl/rtnl-link-set.c84
-rw-r--r--libmnl/examples/rtnl/rtnl-neigh-dump.c158
-rw-r--r--libmnl/examples/rtnl/rtnl-route-add.c127
-rw-r--r--libmnl/examples/rtnl/rtnl-route-dump.c356
-rw-r--r--libmnl/examples/rtnl/rtnl-route-event.c341
-rw-r--r--libmnl/include/Makefile.am1
-rw-r--r--libmnl/include/libmnl/Makefile.am1
-rw-r--r--libmnl/include/libmnl/libmnl.h202
-rw-r--r--libmnl/include/linux/Makefile.am2
-rw-r--r--libmnl/include/linux/can.h298
-rw-r--r--libmnl/include/linux/can/Makefile.am1
-rw-r--r--libmnl/include/linux/can/netlink.h185
-rw-r--r--libmnl/include/linux/netfilter/Makefile.am1
-rw-r--r--libmnl/include/linux/netfilter/nfnetlink_conntrack.h252
-rw-r--r--libmnl/include/linux/netlink.h153
-rw-r--r--libmnl/include/linux/socket.h21
-rw-r--r--libmnl/libmnl.pc.in15
-rw-r--r--libmnl/m4/.gitignore2
-rw-r--r--libmnl/m4/gcc4_visibility.m421
-rw-r--r--libmnl/src/Makefile.am5
-rw-r--r--libmnl/src/attr.c742
-rw-r--r--libmnl/src/callback.c167
-rw-r--r--libmnl/src/internal.h11
-rw-r--r--libmnl/src/libmnl.map79
-rw-r--r--libmnl/src/nlmsg.c587
-rw-r--r--libmnl/src/socket.c351
-rw-r--r--list.h34
-rw-r--r--m4/ax_append_flag.m471
-rw-r--r--m4/ax_check_compile_flag.m474
-rw-r--r--marvell.c40
-rw-r--r--natsemi.c6
-rw-r--r--netlink/bitset.c259
-rw-r--r--netlink/bitset.h28
-rw-r--r--netlink/cable_test.c595
-rw-r--r--netlink/channels.c143
-rw-r--r--netlink/coalesce.c335
-rw-r--r--netlink/desc-ethtool.c595
-rw-r--r--netlink/desc-genlctrl.c113
-rw-r--r--netlink/desc-rtnl.c96
-rw-r--r--netlink/eee.c189
-rw-r--r--netlink/extapi.h136
-rw-r--r--netlink/features.c569
-rw-r--r--netlink/fec.c360
-rw-r--r--netlink/mm.c270
-rw-r--r--netlink/module-eeprom.c305
-rw-r--r--netlink/module.c179
-rw-r--r--netlink/monitor.c324
-rw-r--r--netlink/msgbuff.c256
-rw-r--r--netlink/msgbuff.h123
-rw-r--r--netlink/netlink.c527
-rw-r--r--netlink/netlink.h178
-rw-r--r--netlink/nlsock.c405
-rw-r--r--netlink/nlsock.h45
-rw-r--r--netlink/parser.c1141
-rw-r--r--netlink/parser.h153
-rw-r--r--netlink/pause.c331
-rw-r--r--netlink/permaddr.c114
-rw-r--r--netlink/plca.c296
-rw-r--r--netlink/prettymsg.c262
-rw-r--r--netlink/prettymsg.h146
-rw-r--r--netlink/privflags.c158
-rw-r--r--netlink/pse-pd.c193
-rw-r--r--netlink/rings.c214
-rw-r--r--netlink/rss.c230
-rw-r--r--netlink/settings.c1377
-rw-r--r--netlink/stats.c333
-rw-r--r--netlink/strset.c297
-rw-r--r--netlink/strset.h25
-rw-r--r--netlink/tsinfo.c124
-rw-r--r--netlink/tunnels.c236
-rw-r--r--qsfp.c676
-rw-r--r--qsfp.h105
-rw-r--r--realtek.c2
-rw-r--r--rxclass.c12
-rwxr-xr-xscripts/ethtool-import-uapi67
-rw-r--r--sfc.c3
-rw-r--r--sff-common.c74
-rw-r--r--sff-common.h36
-rw-r--r--sfpdiag.c2
-rw-r--r--sfpid.c159
-rw-r--r--shell-completion/bash/ethtool30
-rw-r--r--smsc911x.c2
-rw-r--r--stmmac.c4
-rw-r--r--test-cmdline.c39
-rw-r--r--test-common.c10
-rw-r--r--test-features.c11
-rw-r--r--tg3.c8
-rw-r--r--tse.c2
-rw-r--r--uapi/linux/const.h36
-rw-r--r--uapi/linux/ethtool.h (renamed from ethtool-copy.h)391
-rw-r--r--uapi/linux/ethtool_netlink.h984
-rw-r--r--uapi/linux/genetlink.h103
-rw-r--r--uapi/linux/hdlc/ioctl.h94
-rw-r--r--uapi/linux/if.h296
-rw-r--r--uapi/linux/if_addr.h77
-rw-r--r--uapi/linux/if_ether.h181
-rw-r--r--uapi/linux/if_link.h1398
-rw-r--r--uapi/linux/libc-compat.h267
-rw-r--r--uapi/linux/neighbour.h224
-rw-r--r--uapi/linux/net_tstamp.h (renamed from net_tstamp-copy.h)68
-rw-r--r--uapi/linux/netlink.h374
-rw-r--r--uapi/linux/posix_types.h38
-rw-r--r--uapi/linux/rtnetlink.h826
-rw-r--r--uapi/linux/socket.h38
-rw-r--r--uapi/linux/stddef.h47
-rw-r--r--uapi/linux/types.h57
-rw-r--r--vioc.c2
-rw-r--r--vmxnet3.c2
186 files changed, 32924 insertions, 1076 deletions
diff --git a/.gitignore b/.gitignore
index c4df588..2679fae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,5 @@ test-*.trs
.*.swp
*.patch
+.dirstamp
+tags
diff --git a/Android.bp b/Android.bp
index 4d4f3b7..54e04d3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
}
diff --git a/LICENSE b/LICENSE
index d68dccb..aa72bc6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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)
diff --git a/NEWS b/NEWS
index 70f2396..e5eca95 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/OWNERS b/OWNERS
index 62c5737..c24680e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/amd8111e.c b/amd8111e.c
index 5a056b3..175516b 100644
--- a/amd8111e.c
+++ b/amd8111e.c
@@ -152,7 +152,7 @@ typedef enum {
#define PHY_SPEED_100 0x3
-int amd8111e_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int amd8111e_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
diff --git a/at76c50x-usb.c b/at76c50x-usb.c
index 0121e98..fad41bf 100644
--- a/at76c50x-usb.c
+++ b/at76c50x-usb.c
@@ -12,7 +12,7 @@ static char *hw_versions[] = {
" 505AMX",
};
-int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u8 version = (u8)(regs->version >> 24);
diff --git a/bnxt.c b/bnxt.c
new file mode 100644
index 0000000..2b0ac76
--- /dev/null
+++ b/bnxt.c
@@ -0,0 +1,97 @@
+/* Code to dump registers for NetXtreme-E/NetXtreme-C Broadcom devices.
+ *
+ * Copyright (c) 2020 Broadcom Inc.
+ */
+#include <stdio.h>
+#include "internal.h"
+
+#define BNXT_PXP_REG_LEN 0x3110
+#define BNXT_PCIE_STATS_LEN (12 * sizeof(u64))
+
+struct bnxt_pcie_stat {
+ const char *name;
+ u16 offset;
+ u8 size;
+ const char *format;
+};
+
+static const struct bnxt_pcie_stat bnxt_pcie_stats[] = {
+ { .name = "PL Signal integrity errors", .offset = 0, .size = 4, .format = "%llu" },
+ { .name = "DL Signal integrity errors", .offset = 4, .size = 4, .format = "%llu" },
+ { .name = "TLP Signal integrity errors", .offset = 8, .size = 4, .format = "%llu" },
+ { .name = "Link integrity", .offset = 12, .size = 4, .format = "%llu" },
+ { .name = "TX TLP traffic rate", .offset = 16, .size = 4, .format = "%llu" },
+ { .name = "RX TLP traffic rate", .offset = 20, .size = 4, .format = "%llu" },
+ { .name = "TX DLLP traffic rate", .offset = 24, .size = 4, .format = "%llu" },
+ { .name = "RX DLLP traffic rate", .offset = 28, .size = 4, .format = "%llu" },
+ { .name = "Equalization Phase 0 time(ms)", .offset = 33, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 1 time(ms)", .offset = 32, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 2 time(ms)", .offset = 35, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 3 time(ms)", .offset = 34, .size = 1, .format = "0x%x" },
+ { .name = "PHY LTSSM Histogram 0", .offset = 36, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 1", .offset = 38, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 2", .offset = 40, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 3", .offset = 42, .size = 2, .format = "0x%x"},
+ { .name = "Recovery Histogram 0", .offset = 44, .size = 2, .format = "0x%x"},
+ { .name = "Recovery Histogram 1", .offset = 46, .size = 2, .format = "0x%x"},
+};
+
+int bnxt_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs)
+{
+ const struct bnxt_pcie_stat *stats = bnxt_pcie_stats;
+ u16 *pcie_stats, pcie_stat16;
+ u32 reg, i, pcie_stat32;
+ u64 pcie_stat64;
+
+ if (regs->len < BNXT_PXP_REG_LEN) {
+ fprintf(stdout, "Length too short, expected at least 0x%x\n",
+ BNXT_PXP_REG_LEN);
+ return -1;
+ }
+
+ fprintf(stdout, "PXP Registers\n");
+ fprintf(stdout, "Offset\tValue\n");
+ fprintf(stdout, "------\t-------\n");
+ for (i = 0; i < BNXT_PXP_REG_LEN; i += sizeof(reg)) {
+ memcpy(&reg, &regs->data[i], sizeof(reg));
+ if (reg)
+ fprintf(stdout, "0x%04x\t0x%08x\n", i, reg);
+ }
+ fprintf(stdout, "\n");
+
+ if (!regs->version)
+ return 0;
+
+ if (regs->len < (BNXT_PXP_REG_LEN + BNXT_PCIE_STATS_LEN)) {
+ fprintf(stdout, "Length is too short, expected 0x%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;
+}
diff --git a/cmis.c b/cmis.c
new file mode 100644
index 0000000..531932e
--- /dev/null
+++ b/cmis.c
@@ -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;
+}
diff --git a/cmis.h b/cmis.h
new file mode 100644
index 0000000..8d66f92
--- /dev/null
+++ b/cmis.h
@@ -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
diff --git a/cpsw.c b/cpsw.c
new file mode 100644
index 0000000..68dcfac
--- /dev/null
+++ b/cpsw.c
@@ -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;
+}
diff --git a/de2104x.c b/de2104x.c
index cc03533..190422f 100644
--- a/de2104x.c
+++ b/de2104x.c
@@ -111,7 +111,7 @@ print_rx_missed(u32 csr8)
}
}
-static void de21040_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+static void de21040_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 tmp, v, *data = (u32 *)regs->data;
@@ -417,7 +417,7 @@ static void de21040_dump_regs(struct ethtool_drvinfo *info maybe_unused,
v & (1<<0) ? " Jabber disable\n" : "");
}
-static void de21041_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+static void de21041_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 tmp, v, *data = (u32 *)regs->data;
diff --git a/dsa.c b/dsa.c
index 50a171b..33c1d39 100644
--- a/dsa.c
+++ b/dsa.c
@@ -3,6 +3,8 @@
#include "internal.h"
+#define SERDES_OFFSET 32
+
/* Macros and dump functions for the 16-bit mv88e6xxx per-port registers */
#define REG(_reg, _name, _val) \
@@ -405,21 +407,204 @@ static void dsa_mv88e6352(int reg, u16 val)
case 19:
REG(reg, "Rx Frame Counter", val);
break;
+ case 20 ... 21:
+ REG(reg, "Reserved", val);
+ break;
case 22:
REG(reg, "LED Control", val);
break;
+ case 23:
+ REG(reg, "Reserved", val);
+ break;
case 24:
REG(reg, "Tag Remap 0-3", val);
break;
case 25:
REG(reg, "Tag Remap 4-7", val);
break;
+ case 26:
+ REG(reg, "Reserved", val);
+ break;
case 27:
REG(reg, "Queue Counters", val);
break;
- default:
+ case 28 ... 31:
REG(reg, "Reserved", val);
break;
+ case SERDES_OFFSET + 0:
+ REG(reg - SERDES_OFFSET, "Fiber Control", val);
+ FIELD("Fiber Reset", "%u", !!(val & 0x8000));
+ FIELD("Loopback", "%u", !!(val & 0x4000));
+ FIELD("Speed", "%s",
+ (val & (0x2000 | 0x0040)) == 0x0000 ? "10 Mbps" :
+ (val & (0x2000 | 0x0040)) == 0x2000 ? "100 Mbps" :
+ (val & (0x2000 | 0x0040)) == 0x0040 ? "1000 Mbps" :
+ (val & (0x2000 | 0x0040)) == (0x2000 | 0x0040) ?
+ "Reserved" : "?");
+ FIELD("Autoneg Enable", "%u", !!(val & 0x1000));
+ FIELD("Power down", "%u", !!(val & 0x0800));
+ FIELD("Isolate", "%u", !!(val & 0x0400));
+ FIELD("Restart Autoneg", "%u", !!(val & 0x0200));
+ FIELD("Duplex", "%s", val & 0x0100 ? "Full" : "Half");
+ break;
+ case SERDES_OFFSET + 1:
+ REG(reg - SERDES_OFFSET, "Fiber Status", val);
+ FIELD("100Base-X FD", "%u", !!(val & 0x4000));
+ FIELD("100Base-X HD", "%u", !!(val & 0x2000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0020));
+ FIELD("Remote Fault", "%u", !!(val & 0x0010));
+ FIELD("Autoneg Ability", "%u", !!(val & 0x0008));
+ FIELD("Link Status", "%s", val & 0x0004 ? "Up" : "Down");
+ break;
+ case SERDES_OFFSET + 2:
+ REG(reg - SERDES_OFFSET, "PHY ID 1", val);
+ break;
+ case SERDES_OFFSET + 3:
+ REG(reg - SERDES_OFFSET, "PHY ID 2", val);
+ break;
+ case SERDES_OFFSET + 4:
+ REG(reg - SERDES_OFFSET, "Fiber Autoneg Advertisement", val);
+ FIELD("Remote Fault", "%s",
+ (val & 0x3000) == 0x0000 ? "No error, link OK" :
+ (val & 0x3000) == 0x1000 ? "Link failure" :
+ (val & 0x3000) == 0x2000 ? "Offline" :
+ (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?");
+ FIELD("Pause", "%s",
+ (val & 0x0180) == 0x0000 ? "No Pause" :
+ (val & 0x0180) == 0x0080 ? "Symmetric Pause" :
+ (val & 0x0180) == 0x0100 ? "Asymmetric Pause" :
+ (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" :
+ "?");
+ FIELD("1000BaseX HD", "%u", !!(val & 0x0040));
+ FIELD("1000BaseX FD", "%u", !!(val & 0x0020));
+ break;
+ case SERDES_OFFSET + 5:
+ REG(reg - SERDES_OFFSET, "Fiber Link Autoneg Ability", val);
+ FIELD("Acknowledge", "%u", !!(val & 0x4000));
+ FIELD("Remote Fault", "%s",
+ (val & 0x3000) == 0x0000 ? "No error, link OK" :
+ (val & 0x3000) == 0x1000 ? "Link failure" :
+ (val & 0x3000) == 0x2000 ? "Offline" :
+ (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?");
+ FIELD("Pause", "%s",
+ (val & 0x0180) == 0x0000 ? "No Pause" :
+ (val & 0x0180) == 0x0080 ? "Symmetric Pause" :
+ (val & 0x0180) == 0x0100 ? "Asymmetric Pause" :
+ (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" :
+ "?");
+ FIELD("1000BaseX HD", "%u", !!(val & 0x0040));
+ FIELD("1000BaseX FD", "%u", !!(val & 0x0020));
+ break;
+ case SERDES_OFFSET + 6:
+ REG(reg - SERDES_OFFSET, "Fiber Autoneg Expansion", val);
+ FIELD("Link Partner Next Page Ability", "%u", !!(val & 0x0008));
+ FIELD("Page Received", "%u", !!(val & 0x0002));
+ FIELD("Link Partner Autoneg Ability", "%u", !!(val & 0x0001));
+ break;
+ case SERDES_OFFSET + 7:
+ REG(reg - SERDES_OFFSET, "Fiber Next Page Transmit", val);
+ break;
+ case SERDES_OFFSET + 8:
+ REG(reg - SERDES_OFFSET, "Fiber Link Partner Next Page", val);
+ break;
+ case SERDES_OFFSET + 9 ... SERDES_OFFSET + 14:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 15:
+ REG(reg - SERDES_OFFSET, "Extended Status", val);
+ break;
+ case SERDES_OFFSET + 16:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Control", val);
+ FIELD("Fiber Transmit FIFO Depth", "%s",
+ (val & 0xc000) == 0x0000 ? "16 Bits" :
+ (val & 0xc000) == 0x4000 ? "24 Bits" :
+ (val & 0xc000) == 0x8000 ? "32 Bits" :
+ (val & 0xc000) == 0xc000 ? "40 Bits" : "?");
+ FIELD("SERDES Loopback", "%u", !!(val & 0x1000));
+ FIELD("Force Link Good", "%u", !!(val & 0x0400));
+ FIELD("MAC Interface Power Down", "%u", !!(val & 0x0008));
+ FIELD("Mode", "%s",
+ (val & 0x0003) == 0x0000 ? "100BaseFX" :
+ (val & 0x0003) == 0x0001 ? "1000BaseX" :
+ (val & 0x0003) == 0x0002 ? "SGMII System" :
+ (val & 0x0003) == 0x0003 ? "SGMII Media" : "?");
+ break;
+ case SERDES_OFFSET + 17:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Status", val);
+ FIELD("Speed", "%s",
+ (val & 0xc000) == 0x0000 ? "10 Mbps" :
+ (val & 0xc000) == 0x4000 ? "100 Mbps" :
+ (val & 0xc000) == 0x8000 ? "1000 Mbps" :
+ (val & 0xc000) == 0xc000 ? "Reserved" : "?");
+ FIELD("Duplex", "%s", val & 0x2000 ? "Full" : "Half");
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Speed/Duplex Resolved", "%u", !!(val & 0x0800));
+ FIELD("Link", "%s", val & 0x0400 ? "Up" : "Down");
+ FIELD("Sync", "%u", !!(val & 0x0020));
+ FIELD("Energy Detect", "%s", val & 0x010 ? "False" : "True");
+ FIELD("Transmit Pause", "%u", !!(val & 0x0008));
+ FIELD("Receive Pause", "%u", !!(val & 0x00004));
+ break;
+ case SERDES_OFFSET + 18:
+ REG(reg - SERDES_OFFSET, "Fiber Interrupt Enable", val);
+ FIELD("Speed Changed", "%u", !!(val & 0x4000));
+ FIELD("Duplex Changed", "%u", !!(val & 0x2000));
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0800));
+ FIELD("Link Status Change", "%u", !!(val & 0x0400));
+ FIELD("Symbol Error", "%u", !!(val & 0x0200));
+ FIELD("False Carrier", "%u", !!(val & 0x0100));
+ FIELD("Energy Detect", "%u", !!(val & 0x0010));
+ break;
+ case SERDES_OFFSET + 19:
+ REG(reg - SERDES_OFFSET, "Fiber Interrupt Status", val);
+ FIELD("Speed Changed", "%u", !!(val & 0x4000));
+ FIELD("Duplex Changed", "%u", !!(val & 0x2000));
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0800));
+ FIELD("Link Status Change", "%u", !!(val & 0x0400));
+ FIELD("Symbol Error", "%u", !!(val & 0x0200));
+ FIELD("False Carrier", "%u", !!(val & 0x0100));
+ FIELD("Energy Detect", "%u", !!(val & 0x0010));
+ break;
+ case SERDES_OFFSET + 20:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 21:
+ REG(reg - SERDES_OFFSET, "Fiber Receive Error Counter", val);
+ break;
+ case SERDES_OFFSET + 22:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 23:
+ REG(reg - SERDES_OFFSET, "PRBS Control", val);
+ break;
+ case SERDES_OFFSET + 24:
+ REG(reg - SERDES_OFFSET, "PRBS Error Counter LSB", val);
+ break;
+ case SERDES_OFFSET + 25:
+ REG(reg - SERDES_OFFSET, "PRBS Error Counter MSB", val);
+ break;
+ case SERDES_OFFSET + 26:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Control 2", val);
+ FIELD("1000BaseX Noise Filtering", "%u", !!(val & 0x4000));
+ FIELD("1000BaseFX Noise Filtering", "%u", !!(val & 0x2000));
+ FIELD("SERDES Autoneg Bypass Enable", "%u", !!(val & 0x0040));
+ FIELD("SERDES Autoneg Bypass Status", "%u", !!(val & 0x0020));
+ FIELD("Fiber Transmitter Disable", "%u", !!(val & 0x0008));
+ FIELD("SGMII/Fiber Output Amplitude", "%s",
+ (val & 0x0007) == 0x0000 ? "14mV" :
+ (val & 0x0007) == 0x0001 ? "112mV" :
+ (val & 0x0007) == 0x0002 ? "210mV" :
+ (val & 0x0007) == 0x0003 ? "308mV" :
+ (val & 0x0007) == 0x0004 ? "406mV" :
+ (val & 0x0007) == 0x0005 ? "504mV" :
+ (val & 0x0007) == 0x0006 ? "602mV" :
+ (val & 0x0007) == 0x0007 ? "700mV" : "?");
+ break;
+ default:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
}
};
@@ -639,11 +824,11 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs)
{
const struct dsa_mv88e6xxx_switch *sw = NULL;
const u16 *data = (u16 *)regs->data;
+ unsigned int i;
u16 id;
- int i;
/* Marvell chips have 32 per-port 16-bit registers */
- if (regs->len < 32 * 2)
+ if (regs->len < 32 * sizeof(u16))
return 1;
id = regs->version & 0xfff0;
@@ -667,6 +852,17 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs)
else
REG(i, "", data[i]);
+ /* Dump the SERDES registers, if provided */
+ if (regs->len > SERDES_OFFSET * sizeof(u16)) {
+ printf("\n%s Switch Port SERDES Registers\n", sw->name);
+ printf("-------------------------------------\n");
+ for (i = SERDES_OFFSET; i < regs->len / 2; i++)
+ if (sw->dump)
+ sw->dump(i, data[i]);
+ else
+ REG(i - SERDES_OFFSET, "", data[i]);
+ }
+
return 0;
}
@@ -674,7 +870,7 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs)
#undef FIELD
#undef REG
-int dsa_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int dsa_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
/* DSA per-driver register dump */
diff --git a/e100.c b/e100.c
index 540ae35..fd4bd03 100644
--- a/e100.c
+++ b/e100.c
@@ -36,7 +36,7 @@
#define CU_CMD 0x00F0
#define RU_CMD 0x0007
-int e100_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int e100_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/e1000.c b/e1000.c
index 91e5bc1..dbd6eb5 100644
--- a/e1000.c
+++ b/e1000.c
@@ -363,7 +363,7 @@ static enum e1000_mac_type e1000_get_mac_type(u16 device_id)
return mac_type;
}
-int e1000_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int e1000_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/et131x.c b/et131x.c
index 1b06071..a23f7a2 100644
--- a/et131x.c
+++ b/et131x.c
@@ -2,7 +2,7 @@
#include <string.h>
#include "internal.h"
-int et131x_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int et131x_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u8 version = (u8)(regs->version >> 24);
diff --git a/ethtool.8.in b/ethtool.8.in
index 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
diff --git a/ethtool.c b/ethtool.c
index acf183d..af51220 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -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
diff --git a/fec.c b/fec.c
index 22bc09f..d2373d6 100644
--- a/fec.c
+++ b/fec.c
@@ -194,11 +194,11 @@ static void fec_dump_reg_v2(int reg, u32 val)
#undef FIELD
#undef REG
-int fec_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int fec_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
const u32 *data = (u32 *)regs->data;
- int offset;
+ unsigned int offset;
u32 val;
for (offset = 0; offset < regs->len; offset += 4) {
diff --git a/fec_8xx.c b/fec_8xx.c
index 02ecaef..63352fc 100644
--- a/fec_8xx.c
+++ b/fec_8xx.c
@@ -47,7 +47,7 @@ struct fec {
(unsigned long)(offsetof(struct fec, x)), \
#x, f->x)
-int fec_8xx_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int fec_8xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
struct fec *f = (struct fec *)regs->data;
diff --git a/fjes.c b/fjes.c
index 4c5f6bc..05bd245 100644
--- a/fjes.c
+++ b/fjes.c
@@ -2,7 +2,7 @@
#include <stdio.h>
#include "internal.h"
-int fjes_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int fjes_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/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;
+}
diff --git a/hns3.c b/hns3.c
new file mode 100644
index 0000000..7199426
--- /dev/null
+++ b/hns3.c
@@ -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;
+}
diff --git a/ibm_emac.c b/ibm_emac.c
index 3259c17..9f7cae6 100644
--- a/ibm_emac.c
+++ b/ibm_emac.c
@@ -238,7 +238,7 @@ static void *print_mal_regs(void *buf)
{
struct emac_ethtool_regs_subhdr *hdr = buf;
struct mal_regs *p = (struct mal_regs *)(hdr + 1);
- int i;
+ unsigned int i;
printf("MAL%d Registers\n", hdr->index);
printf("-----------------\n");
@@ -314,7 +314,7 @@ static void *print_tah_regs(void *buf)
return p + 1;
}
-int ibm_emac_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int ibm_emac_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
struct emac_ethtool_regs_hdr *hdr =
diff --git a/igb.c b/igb.c
index 89b5cdb..f358f53 100644
--- a/igb.c
+++ b/igb.c
@@ -88,7 +88,7 @@
#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
-int igb_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int igb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/igc.c b/igc.c
new file mode 100644
index 0000000..1550ac0
--- /dev/null
+++ b/igc.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2020 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+#define RAH_RAH 0x0000FFFF
+#define RAH_ASEL 0x00010000
+#define RAH_QSEL 0x000C0000
+#define RAH_QSEL_EN 0x10000000
+#define RAH_AV 0x80000000
+#define RCTL_RXEN 0x00000002
+#define RCTL_SBP 0x00000004
+#define RCTL_UPE 0x00000008
+#define RCTL_MPE 0x00000010
+#define RCTL_LPE 0x00000020
+#define RCTL_LBM 0x000000C0
+#define RCTL_LBM_PHY 0x00000000
+#define RCTL_LBM_MAC 0x00000040
+#define RCTL_HSEL 0x00000300
+#define RCTL_HSEL_MULTICAST 0x00000000
+#define RCTL_HSEL_UNICAST 0x00000100
+#define RCTL_HSEL_BOTH 0x00000200
+#define RCTL_MO 0x00003000
+#define RCTL_MO_47_36 0x00000000
+#define RCTL_MO_43_32 0x00001000
+#define RCTL_MO_39_28 0x00002000
+#define RCTL_BAM 0x00008000
+#define RCTL_BSIZE 0x00030000
+#define RCTL_BSIZE_2048 0x00000000
+#define RCTL_BSIZE_1024 0x00010000
+#define RCTL_BSIZE_512 0x00020000
+#define RCTL_VFE 0x00040000
+#define RCTL_CFIEN 0x00080000
+#define RCTL_CFI 0x00100000
+#define RCTL_PSP 0x00200000
+#define RCTL_DPF 0x00400000
+#define RCTL_PMCF 0x00800000
+#define RCTL_SECRC 0x04000000
+#define VLANPQF_VP0QSEL 0x00000003
+#define VLANPQF_VP0PBSEL 0x00000004
+#define VLANPQF_VLANP0V 0x00000008
+#define VLANPQF_VP1QSEL 0x00000030
+#define VLANPQF_VP1PBSEL 0x00000040
+#define VLANPQF_VLANP1V 0x00000080
+#define VLANPQF_VP2QSEL 0x00000300
+#define VLANPQF_VP2PBSEL 0x00000400
+#define VLANPQF_VLANP2V 0x00000800
+#define VLANPQF_VP3QSEL 0x00003000
+#define VLANPQF_VP3PBSEL 0x00004000
+#define VLANPQF_VLANP3V 0x00008000
+#define VLANPQF_VP4QSEL 0x00030000
+#define VLANPQF_VP4PBSEL 0x00040000
+#define VLANPQF_VLANP4V 0x00080000
+#define VLANPQF_VP5QSEL 0x00300000
+#define VLANPQF_VP5PBSEL 0x00400000
+#define VLANPQF_VLANP5V 0x00800000
+#define VLANPQF_VP6QSEL 0x03000000
+#define VLANPQF_VP6PBSEL 0x04000000
+#define VLANPQF_VLANP6V 0x08000000
+#define VLANPQF_VP7QSEL 0x30000000
+#define VLANPQF_VP7PBSEL 0x40000000
+#define VLANPQF_VLANP7V 0x80000000
+#define ETQF_ETYPE 0x0000FFFF
+#define ETQF_QUEUE 0x00070000
+#define ETQF_ETYPE_LEN 0x01F00000
+#define ETQF_ETYPE_LEN_EN 0x02000000
+#define ETQF_FILTER_EN 0x04000000
+#define ETQF_IMMEDIATE_INTR 0x20000000
+#define ETQF_1588_TIMESTAMP 0x40000000
+#define ETQF_QUEUE_EN 0x80000000
+
+#define RAH_QSEL_SHIFT 18
+#define VLANPQF_VP1QSEL_SHIFT 4
+#define VLANPQF_VP2QSEL_SHIFT 8
+#define VLANPQF_VP3QSEL_SHIFT 12
+#define VLANPQF_VP4QSEL_SHIFT 16
+#define VLANPQF_VP5QSEL_SHIFT 20
+#define VLANPQF_VP6QSEL_SHIFT 24
+#define VLANPQF_VP7QSEL_SHIFT 28
+#define ETQF_QUEUE_SHIFT 16
+#define ETQF_ETYPE_LEN_SHIFT 20
+
+static const char *bit_to_boolean(u32 val)
+{
+ return val ? "yes" : "no";
+}
+
+static const char *bit_to_enable(u32 val)
+{
+ return val ? "enabled" : "disabled";
+}
+
+static const char *bit_to_prio(u32 val)
+{
+ return val ? "low" : "high";
+}
+
+int igc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 reg;
+ int offset, i;
+ u32 *regs_buff = (u32 *)regs->data;
+ u8 version = (u8)(regs->version >> 24);
+
+ if (version != 2)
+ return -1;
+
+ for (offset = 0; offset < 24; offset++) {
+ reg = regs_buff[offset];
+ printf("%04d: 0x%08X\n", offset, reg);
+ }
+
+ offset = 24;
+
+ reg = regs_buff[offset];
+ printf("%04d: RCTL (Receive Control Register) \n"
+ " Receiver: %s\n"
+ " Stop Bad Packets: %s\n"
+ " Unicast Promiscuous: %s\n"
+ " Multicast Promiscuous: %s\n"
+ " Long Packet Reception: %s\n"
+ " Loopback Model: %s\n"
+ " Hash Select for MTA: %s\n"
+ " Multicast/Unicast Table Offset: %s\n"
+ " Broadcast Accept Mode: %s\n"
+ " Receive Buffer Size: %s\n"
+ " VLAN Filter: %s\n"
+ " Canonical Form Indicator: %s\n"
+ " Canonical Form Indicator Bit: %s\n"
+ " Pad Small Receive Packets: %s\n"
+ " Discard Pause Frames: %s\n"
+ " Pass MAC Control Frames: %s\n"
+ " Strip Ethernet CRC: %s\n",
+ offset,
+ bit_to_enable(reg & RCTL_RXEN),
+ bit_to_enable(reg & RCTL_SBP),
+ bit_to_enable(reg & RCTL_UPE),
+ bit_to_enable(reg & RCTL_MPE),
+ bit_to_enable(reg & RCTL_LPE),
+ (reg & RCTL_LBM) == RCTL_LBM_PHY ? "PHY" :
+ (reg & RCTL_LBM) == RCTL_LBM_MAC ? "MAC" :
+ "undefined",
+ (reg & RCTL_HSEL) == RCTL_HSEL_MULTICAST ? "multicast only" :
+ (reg & RCTL_HSEL) == RCTL_HSEL_UNICAST ? "unicast only" :
+ (reg & RCTL_HSEL) == RCTL_HSEL_BOTH ? "multicast and unicast" :
+ "reserved",
+ (reg & RCTL_MO) == RCTL_MO_47_36 ? "bits [47:36]" :
+ (reg & RCTL_MO) == RCTL_MO_43_32 ? "bits [43:32]" :
+ (reg & RCTL_MO) == RCTL_MO_39_28 ? "bits [39:28]" :
+ "bits [35:24]",
+ bit_to_enable(reg & RCTL_BAM),
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_2048 ? "2048 bytes" :
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_1024 ? "1024 bytes" :
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_512 ? "512 bytes" :
+ "256 bytes",
+ bit_to_enable(reg & RCTL_VFE),
+ bit_to_enable(reg & RCTL_CFIEN),
+ reg & RCTL_CFI ? "discarded" : "accepted",
+ bit_to_enable(reg & RCTL_PSP),
+ bit_to_enable(reg & RCTL_DPF),
+ bit_to_enable(reg & RCTL_PMCF),
+ bit_to_enable(reg & RCTL_SECRC));
+
+ for (offset = 25; offset < 172; offset++) {
+ reg = regs_buff[offset];
+ printf("%04d: 0x%08X\n", offset, reg);
+ }
+
+ offset = 172;
+
+ for (i = 0; i < 16; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: RAL (Receive Address Low %02d) \n"
+ " Receive Address Low: %08X\n",
+ offset + i, i,
+ reg);
+ }
+
+ offset = 188;
+
+ for (i = 0; i < 16; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: RAH (Receive Address High %02d) \n"
+ " Receive Address High: %04X\n"
+ " Address Select: %s\n"
+ " Queue Select: %d\n"
+ " Queue Select Enable: %s\n"
+ " Address Valid: %s\n",
+ offset + i, i,
+ reg & RAH_RAH,
+ reg & RAH_ASEL ? "source" : "destination",
+ (reg & RAH_QSEL) >> RAH_QSEL_SHIFT,
+ bit_to_boolean(reg & RAH_QSEL_EN),
+ bit_to_boolean(reg & RAH_AV));
+ }
+
+ offset = 204;
+
+ reg = regs_buff[offset];
+ printf("%04d: VLANPQF (VLAN Priority Queue Filter) \n"
+ " Priority 0 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 1 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 2 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 3 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 4 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 5 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 6 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 7 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n",
+ offset,
+ reg & VLANPQF_VP0QSEL,
+ bit_to_prio(reg & VLANPQF_VP0PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP0V),
+ (reg & VLANPQF_VP1QSEL) >> VLANPQF_VP1QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP1PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP1V),
+ (reg & VLANPQF_VP2QSEL) >> VLANPQF_VP2QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP2PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP2V),
+ (reg & VLANPQF_VP3QSEL) >> VLANPQF_VP3QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP3PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP3V),
+ (reg & VLANPQF_VP4QSEL) >> VLANPQF_VP4QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP4PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP4V),
+ (reg & VLANPQF_VP5QSEL) >> VLANPQF_VP5QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP5PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP5V),
+ (reg & VLANPQF_VP6QSEL) >> VLANPQF_VP6QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP6PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP6V),
+ (reg & VLANPQF_VP7QSEL) >> VLANPQF_VP7QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP7PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP7V));
+
+ offset = 205;
+
+ for (i = 0; i < 8; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: ETQF (EType Queue Filter %d) \n"
+ " EType: %04X\n"
+ " EType Length: %d\n"
+ " EType Length Enable: %s\n"
+ " Queue: %d\n"
+ " Queue Enable: %s\n"
+ " Immediate Interrupt: %s\n"
+ " 1588 Time Stamp: %s\n"
+ " Filter Enable: %s\n",
+ offset + i, i,
+ reg & ETQF_ETYPE,
+ (reg & ETQF_ETYPE_LEN) >> ETQF_ETYPE_LEN_SHIFT,
+ bit_to_boolean(reg & ETQF_ETYPE_LEN_EN),
+ (reg & ETQF_QUEUE) >> ETQF_QUEUE_SHIFT,
+ bit_to_boolean(reg & ETQF_QUEUE_EN),
+ bit_to_enable(reg & ETQF_IMMEDIATE_INTR),
+ bit_to_enable(reg & ETQF_1588_TIMESTAMP),
+ bit_to_boolean(reg & ETQF_FILTER_EN));
+ }
+
+ return 0;
+}
diff --git a/internal.h b/internal.h
index ff52c6e..4b994f5 100644
--- a/internal.h
+++ b/internal.h
@@ -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__ */
diff --git a/ixgb.c b/ixgb.c
index 7c16c6e..8aec9a9 100644
--- a/ixgb.c
+++ b/ixgb.c
@@ -38,7 +38,7 @@
#define IXGB_RAH_ASEL_SRC 0x00010000
#define IXGB_RAH_AV 0x80000000
-int ixgb_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int ixgb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/ixgbe.c b/ixgbe.c
index 9754b2a..6d509c8 100644
--- a/ixgbe.c
+++ b/ixgbe.c
@@ -168,7 +168,7 @@ ixgbe_get_mac_type(u16 device_id)
}
int
-ixgbe_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+ixgbe_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/ixgbevf.c b/ixgbevf.c
index 265e0bf..91c2b2c 100644
--- a/ixgbevf.c
+++ b/ixgbevf.c
@@ -3,7 +3,7 @@
#include "internal.h"
int
-ixgbevf_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+ixgbevf_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/json_print.c b/json_print.c
new file mode 100644
index 0000000..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;
+}
diff --git a/lan78xx.c b/lan78xx.c
index 46ade1c..75ee048 100644
--- a/lan78xx.c
+++ b/lan78xx.c
@@ -2,7 +2,7 @@
#include <string.h>
#include "internal.h"
-int lan78xx_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int lan78xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
unsigned int *lan78xx_reg = (unsigned int *)regs->data;
diff --git a/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), &params);
+
+ 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), &params);
+
+ 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);
+}
+
+/**
+ * @}
+ */
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..aa97fdd
--- /dev/null
+++ b/list.h
@@ -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
diff --git a/marvell.c b/marvell.c
index 9e5440d..3f3aed8 100644
--- a/marvell.c
+++ b/marvell.c
@@ -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;
diff --git a/natsemi.c b/natsemi.c
index ce82c42..4d9fc09 100644
--- a/natsemi.c
+++ b/natsemi.c
@@ -323,7 +323,7 @@ static void __print_intr(int d, int intr, const char *name,
} while (0)
int
-natsemi_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+natsemi_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *data = (u32 *)regs->data;
@@ -964,11 +964,11 @@ natsemi_dump_regs(struct ethtool_drvinfo *info maybe_unused,
}
int
-natsemi_dump_eeprom(struct ethtool_drvinfo *info maybe_unused,
+natsemi_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_eeprom *ee)
{
- int i;
u16 *eebuf = (u16 *)ee->data;
+ unsigned int i;
if (ee->magic != NATSEMI_MAGIC) {
fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n",
diff --git a/netlink/bitset.c b/netlink/bitset.c
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);
+}
diff --git a/qsfp.c b/qsfp.c
index d0774b0..5a535c5 100644
--- a/qsfp.c
+++ b/qsfp.c
@@ -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;
+}
diff --git a/qsfp.h b/qsfp.h
index b623174..9f0cb0f 100644
--- a/qsfp.h
+++ b/qsfp.h
@@ -31,14 +31,6 @@
#define SFF8636_ID_OFFSET 0x00
#define SFF8636_REV_COMPLIANCE_OFFSET 0x01
-#define SFF8636_REV_UNSPECIFIED 0x00
-#define SFF8636_REV_8436_48 0x01
-#define SFF8636_REV_8436_8636 0x02
-#define SFF8636_REV_8636_13 0x03
-#define SFF8636_REV_8636_14 0x04
-#define SFF8636_REV_8636_15 0x05
-#define SFF8636_REV_8636_20 0x06
-#define SFF8636_REV_8636_27 0x07
#define SFF8636_STATUS_2_OFFSET 0x02
/* Flat Memory:0- Paging, 1- Page 0 only */
@@ -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
diff --git a/realtek.c b/realtek.c
index d10cfd4..ee0c611 100644
--- a/realtek.c
+++ b/realtek.c
@@ -241,7 +241,7 @@ print_intr_bits(u16 mask)
}
int
-realtek_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+realtek_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *data = (u32 *) regs->data;
diff --git a/rxclass.c b/rxclass.c
index 7997265..f17e3a5 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -348,8 +348,9 @@ int rxclass_rule_getall(struct cmd_context *ctx)
{
struct ethtool_rxnfc *nfccmd;
__u32 *rule_locs;
- int err, i;
+ unsigned int i;
__u32 count;
+ int err;
/* determine rule count */
err = rxclass_get_dev_info(ctx, &count, NULL);
@@ -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
diff --git a/sfc.c b/sfc.c
index f56243d..340800e 100644
--- a/sfc.c
+++ b/sfc.c
@@ -3890,7 +3890,8 @@ print_complex_table(unsigned revision, const struct efx_nic_reg_table *table,
}
int
-sfc_dump_regs(struct ethtool_drvinfo *info maybe_unused, struct ethtool_regs *regs)
+sfc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
{
const struct efx_nic_reg *reg;
const struct efx_nic_reg_table *table;
diff --git a/sff-common.c b/sff-common.c
index 7700cbe..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__ */
diff --git a/sfpdiag.c b/sfpdiag.c
index fa41651..1fa8b7b 100644
--- a/sfpdiag.c
+++ b/sfpdiag.c
@@ -190,8 +190,8 @@ static float befloattoh(const __u32 *source)
static void sff8472_calibration(const __u8 *id, struct sff_diags *sd)
{
- int i;
__u16 rx_reading;
+ unsigned int i;
/* Calibration should occur for all values (threshold and current) */
for (i = 0; i < ARRAY_SIZE(sd->bias_cur); ++i) {
diff --git a/sfpid.c b/sfpid.c
index a1753d3..1bc45c1 100644
--- a/sfpid.c
+++ b/sfpid.c
@@ -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
diff --git a/smsc911x.c b/smsc911x.c
index bafee21..b643504 100644
--- a/smsc911x.c
+++ b/smsc911x.c
@@ -2,7 +2,7 @@
#include <string.h>
#include "internal.h"
-int smsc911x_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int smsc911x_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
unsigned int *smsc_reg = (unsigned int *)regs->data;
diff --git a/stmmac.c b/stmmac.c
index 98d9058..5847120 100644
--- a/stmmac.c
+++ b/stmmac.c
@@ -18,7 +18,7 @@
#define GMAC_REG_NUM 55
#define GMAC_DMA_REG_NUM 23
-int st_mac100_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int st_mac100_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
int i;
@@ -51,7 +51,7 @@ int st_mac100_dump_regs(struct ethtool_drvinfo *info maybe_unused,
return 0;
}
-int st_gmac_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int st_gmac_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
int i;
diff --git a/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;
diff --git a/tg3.c b/tg3.c
index 8698391..ebdef2d 100644
--- a/tg3.c
+++ b/tg3.c
@@ -4,10 +4,10 @@
#define TG3_MAGIC 0x669955aa
-int tg3_dump_eeprom(struct ethtool_drvinfo *info maybe_unused,
+int tg3_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_eeprom *ee)
{
- int i;
+ unsigned int i;
if (ee->magic != TG3_MAGIC) {
fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n",
@@ -23,10 +23,10 @@ int tg3_dump_eeprom(struct ethtool_drvinfo *info maybe_unused,
return 0;
}
-int tg3_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int tg3_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
- int i;
+ unsigned int i;
u32 reg;
fprintf(stdout, "Offset\tValue\n");
diff --git a/tse.c b/tse.c
index e5241ee..fb00d21 100644
--- a/tse.c
+++ b/tse.c
@@ -25,7 +25,7 @@ bitset(u32 val, int bit)
return 0;
}
-int altera_tse_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int altera_tse_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
int i;
diff --git a/uapi/linux/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 */
diff --git a/vioc.c b/vioc.c
index ef163ab..c04a6dc 100644
--- a/vioc.c
+++ b/vioc.c
@@ -11,7 +11,7 @@ struct regs_line {
#define VIOC_REGS_LINE_SIZE sizeof(struct regs_line)
-int vioc_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int vioc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
unsigned int i;
diff --git a/vmxnet3.c b/vmxnet3.c
index c972145..6872682 100644
--- a/vmxnet3.c
+++ b/vmxnet3.c
@@ -3,7 +3,7 @@
#include "internal.h"
int
-vmxnet3_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+vmxnet3_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;