netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release
@ 2020-03-04 20:24 Michal Kubecek
  2020-03-04 20:24 ` [PATCH ethtool v2 01/25] move UAPI header copies to a separate directory Michal Kubecek
                   ` (26 more replies)
  0 siblings, 27 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:24 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

This series adds initial support for ethtool netlink interface provided by
kernel since 5.6-rc1. The traditional ioctl interface is still supported
for compatibility with older kernels. The netlink interface and message
formats are documented in Documentation/networking/ethtool-netlink.rst file
in kernel source tree.

Netlink interface is preferred but ethtool falls back to ioctl if netlink
interface is not available (i.e. the "ethtool" genetlink family is not
registered). It also falls back if a particular command is not implemented
in netlink (kernel returns -EOPNOTSUPP). This allows new ethtool versions
to work with older kernel versions while support for ethool commands is
added in steps.

The series aims to touch existing ioctl code as little as possible in the
first phase to minimize the risk of introducing regressions. It is also
possible to build ethtool without netlink support if --disable-netlink is
passed to configure script. The most visible changes to existing code are

  - UAPI header copies are moved to uapi/ under original names
  - some variables and functions which are going to be shared with netlink
    code are moved from ethtool.c to common.c and common.h
  - args[] array in ethtool.c was rewritten to use named initializers

Except for changes to main(), all netlink specific code is in a separate
directory netlink/ and is divided into multiple files.

Changes in v2:
- add support for permanent hardware addres ("ethtool -P", patch 20)
- add support for pretty printing of netlink messages (patches 21-25)
- make output of "ethtool <dev>" closer to ioctl implementation
- two more kernel uapi header copies (patch 5)
- support for rtnetlink socket and requests (needed for "ethtool -P")
- some kerneldoc style comments

Michal Kubecek (25):
  move UAPI header copies to a separate directory
  update UAPI header copies
  add --debug option to control debugging messages
  use named initializers in command line option list
  netlink: add netlink related UAPI header files
  netlink: introduce the netlink interface
  netlink: message buffer and composition helpers
  netlink: netlink socket wrapper and helpers
  netlink: initialize ethtool netlink socket
  netlink: add support for string sets
  netlink: add notification monitor
  move shared code into a common file
  netlink: add bitset helpers
  netlink: partial netlink handler for gset (no option)
  netlink: support getting wake-on-lan and debugging settings
  netlink: add basic command line parsing helpers
  netlink: add bitset command line parser handlers
  netlink: add netlink handler for sset (-s)
  netlink: support tests with netlink enabled
  netlink: add handler for permaddr (-P)
  netlink: support for pretty printing netlink messages
  netlink: message format description for ethtool netlink
  netlink: message format descriptions for genetlink control
  netlink: message format descriptions for rtnetlink
  netlink: use pretty printing for ethtool netlink messages

 Makefile.am                                  |   31 +-
 common.c                                     |  145 +++
 common.h                                     |   26 +
 configure.ac                                 |   14 +-
 ethtool.8.in                                 |   48 +-
 ethtool.c                                    |  819 ++++++++------
 internal.h                                   |   31 +-
 netlink/bitset.c                             |  218 ++++
 netlink/bitset.h                             |   26 +
 netlink/desc-ethtool.c                       |  139 +++
 netlink/desc-genlctrl.c                      |   56 +
 netlink/desc-rtnl.c                          |   96 ++
 netlink/extapi.h                             |   46 +
 netlink/monitor.c                            |  229 ++++
 netlink/msgbuff.c                            |  255 +++++
 netlink/msgbuff.h                            |  117 ++
 netlink/netlink.c                            |  216 ++++
 netlink/netlink.h                            |   87 ++
 netlink/nlsock.c                             |  405 +++++++
 netlink/nlsock.h                             |   45 +
 netlink/parser.c                             | 1058 ++++++++++++++++++
 netlink/parser.h                             |  144 +++
 netlink/permaddr.c                           |  114 ++
 netlink/prettymsg.c                          |  237 ++++
 netlink/prettymsg.h                          |  118 ++
 netlink/settings.c                           |  955 ++++++++++++++++
 netlink/strset.c                             |  297 +++++
 netlink/strset.h                             |   25 +
 test-cmdline.c                               |   29 +-
 test-features.c                              |   11 +
 ethtool-copy.h => uapi/linux/ethtool.h       |   17 +
 uapi/linux/ethtool_netlink.h                 |  237 ++++
 uapi/linux/genetlink.h                       |   89 ++
 uapi/linux/if_link.h                         | 1051 +++++++++++++++++
 net_tstamp-copy.h => uapi/linux/net_tstamp.h |   27 +
 uapi/linux/netlink.h                         |  248 ++++
 uapi/linux/rtnetlink.h                       |  777 +++++++++++++
 37 files changed, 8102 insertions(+), 381 deletions(-)
 create mode 100644 common.c
 create mode 100644 common.h
 create mode 100644 netlink/bitset.c
 create mode 100644 netlink/bitset.h
 create mode 100644 netlink/desc-ethtool.c
 create mode 100644 netlink/desc-genlctrl.c
 create mode 100644 netlink/desc-rtnl.c
 create mode 100644 netlink/extapi.h
 create mode 100644 netlink/monitor.c
 create mode 100644 netlink/msgbuff.c
 create mode 100644 netlink/msgbuff.h
 create mode 100644 netlink/netlink.c
 create mode 100644 netlink/netlink.h
 create mode 100644 netlink/nlsock.c
 create mode 100644 netlink/nlsock.h
 create mode 100644 netlink/parser.c
 create mode 100644 netlink/parser.h
 create mode 100644 netlink/permaddr.c
 create mode 100644 netlink/prettymsg.c
 create mode 100644 netlink/prettymsg.h
 create mode 100644 netlink/settings.c
 create mode 100644 netlink/strset.c
 create mode 100644 netlink/strset.h
 rename ethtool-copy.h => uapi/linux/ethtool.h (99%)
 create mode 100644 uapi/linux/ethtool_netlink.h
 create mode 100644 uapi/linux/genetlink.h
 create mode 100644 uapi/linux/if_link.h
 rename net_tstamp-copy.h => uapi/linux/net_tstamp.h (84%)
 create mode 100644 uapi/linux/netlink.h
 create mode 100644 uapi/linux/rtnetlink.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 01/25] move UAPI header copies to a separate directory
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
@ 2020-03-04 20:24 ` Michal Kubecek
  2020-03-04 20:24 ` [PATCH ethtool v2 02/25] update UAPI header copies Michal Kubecek
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:24 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

The upcoming netlink series is going to add more local copies of kernel
UAPI header files and some of them are going to include others. Keeping
them in the main directory under modified name would require modifying
those includes as well which would be impractical.

Create a subdirectory uapi and move the UAPI headers there to allow
including them in the usual way.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am                                  | 10 +++++-----
 internal.h                                   |  4 ++--
 ethtool-copy.h => uapi/linux/ethtool.h       |  0
 net_tstamp-copy.h => uapi/linux/net_tstamp.h |  0
 4 files changed, 7 insertions(+), 7 deletions(-)
 rename ethtool-copy.h => uapi/linux/ethtool.h (100%)
 rename net_tstamp-copy.h => uapi/linux/net_tstamp.h (100%)

diff --git a/Makefile.am b/Makefile.am
index 3af4d4c2b5da..05beb22be669 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,12 +1,12 @@
-AM_CFLAGS = -Wall
+AM_CFLAGS = -I./uapi -Wall
 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 rxclass.c
 if ETHTOOL_ENABLE_PRETTY_DUMP
 ethtool_SOURCES += \
 		  amd8111e.c de2104x.c dsa.c e100.c e1000.c et131x.c igb.c	\
@@ -25,9 +25,9 @@ endif
 TESTS = test-cmdline test-features
 check_PROGRAMS = test-cmdline test-features
 test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES) 
-test_cmdline_CFLAGS = -DTEST_ETHTOOL
+test_cmdline_CFLAGS = $(AM_FLAGS) -DTEST_ETHTOOL
 test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES) 
-test_features_CFLAGS = -DTEST_ETHTOOL
+test_features_CFLAGS = $(AM_FLAGS) -DTEST_ETHTOOL
 
 dist-hook:
 	cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/internal.h b/internal.h
index ff52c6e7660c..527245633338 100644
--- a/internal.h
+++ b/internal.h
@@ -44,8 +44,8 @@ typedef int32_t s32;
 #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)
diff --git a/ethtool-copy.h b/uapi/linux/ethtool.h
similarity index 100%
rename from ethtool-copy.h
rename to uapi/linux/ethtool.h
diff --git a/net_tstamp-copy.h b/uapi/linux/net_tstamp.h
similarity index 100%
rename from net_tstamp-copy.h
rename to uapi/linux/net_tstamp.h
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 02/25] update UAPI header copies
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
  2020-03-04 20:24 ` [PATCH ethtool v2 01/25] move UAPI header copies to a separate directory Michal Kubecek
@ 2020-03-04 20:24 ` Michal Kubecek
  2020-03-04 20:24 ` [PATCH ethtool v2 03/25] add --debug option to control debugging messages Michal Kubecek
                   ` (24 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:24 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Update to v5.6-rc1.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 uapi/linux/ethtool.h    | 17 +++++++++++++++++
 uapi/linux/net_tstamp.h | 27 +++++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h
index 9afd2e6c5eea..16198061d723 100644
--- a/uapi/linux/ethtool.h
+++ b/uapi/linux/ethtool.h
@@ -591,6 +591,9 @@ struct ethtool_pauseparam {
  * @ETH_SS_RSS_HASH_FUNCS: RSS hush function 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
  */
 enum ethtool_stringset {
 	ETH_SS_TEST		= 0,
@@ -602,6 +605,12 @@ 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,
+
+	/* add new constants above here */
+	ETH_SS_COUNT
 };
 
 /**
@@ -1505,6 +1514,11 @@ 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,
 
 	/* must be last entry */
 	__ETHTOOL_LINK_MODE_MASK_NBITS
@@ -1616,6 +1630,7 @@ enum ethtool_link_mode_bit_indices {
 #define SPEED_56000		56000
 #define SPEED_100000		100000
 #define SPEED_200000		200000
+#define SPEED_400000		400000
 
 #define SPEED_UNKNOWN		-1
 
@@ -1680,6 +1695,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) */
diff --git a/uapi/linux/net_tstamp.h b/uapi/linux/net_tstamp.h
index 3d421d912193..f96e650d0af9 100644
--- a/uapi/linux/net_tstamp.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
  *
@@ -89,6 +90,14 @@ 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,
 };
 
 /* possible values for hwtstamp_config->rx_filter */
@@ -140,4 +149,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 */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 03/25] add --debug option to control debugging messages
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
  2020-03-04 20:24 ` [PATCH ethtool v2 01/25] move UAPI header copies to a separate directory Michal Kubecek
  2020-03-04 20:24 ` [PATCH ethtool v2 02/25] update UAPI header copies Michal Kubecek
@ 2020-03-04 20:24 ` Michal Kubecek
  2020-03-04 20:24 ` [PATCH ethtool v2 04/25] use named initializers in command line option list Michal Kubecek
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:24 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Introduce --debug option to control which debugging messages will be shown.
Argument is a number which is interpreted as bit mask; default value is
zero (i.e. no debug messages).

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 ethtool.8.in | 12 ++++++++++++
 ethtool.c    | 18 ++++++++++++++++--
 internal.h   |  6 ++++++
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/ethtool.8.in b/ethtool.8.in
index 94364c626330..680cad9fbb8f 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -129,6 +129,10 @@ ethtool \- query or control network driver and hardware settings
 .HP
 .B ethtool \-\-version
 .HP
+.B ethtool
+.BN --debug
+.I args
+.HP
 .B ethtool \-a|\-\-show\-pause
 .I devname
 .HP
@@ -437,6 +441,14 @@ 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
 .B \-a \-\-show\-pause
 Queries the specified Ethernet device for pause parameter information.
 .TP
diff --git a/ethtool.c b/ethtool.c
index acf183dc5586..dd0242b27a2f 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5493,10 +5493,10 @@ static int show_usage(struct cmd_context *ctx maybe_unused)
 	fprintf(stdout, PACKAGE " version " VERSION "\n");
 	fprintf(stdout,
 		"Usage:\n"
-		"        ethtool DEVNAME\t"
+		"        ethtool [ --debug MASK ] DEVNAME\t"
 		"Display standard information about device\n");
 	for (i = 0; args[i].opts; i++) {
-		fputs("        ethtool ", stdout);
+		fputs("        ethtool [ --debug MASK ] ", stdout);
 		fprintf(stdout, "%s %s\t%s\n",
 			args[i].opts,
 			args[i].want_device ? "DEVNAME" : "\t",
@@ -5712,6 +5712,20 @@ int main(int argc, char **argp)
 	argp++;
 	argc--;
 
+	ctx.debug = 0;
+	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;
+	}
+
 	/* First argument must be either a valid option or a device
 	 * name to get settings for (which we don't expect to begin
 	 * with '-').
diff --git a/internal.h b/internal.h
index 527245633338..9ec145f55dcb 100644
--- a/internal.h
+++ b/internal.h
@@ -107,6 +107,11 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr)
 #define SIOCETHTOOL     0x8946
 #endif
 
+/* debugging flags */
+enum {
+	DEBUG_PARSE,
+};
+
 /* Internal values for old-style offload flags.  Values and names
  * must not clash with the flags defined for ETHTOOL_{G,S}FLAGS.
  */
@@ -197,6 +202,7 @@ struct cmd_context {
 	struct ifreq ifr;	/* ifreq suitable for ethtool ioctl */
 	int argc;		/* number of arguments to the sub-command */
 	char **argp;		/* arguments to the sub-command */
+	unsigned long debug;	/* debugging mask */
 };
 
 #ifdef TEST_ETHTOOL
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 04/25] use named initializers in command line option list
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (2 preceding siblings ...)
  2020-03-04 20:24 ` [PATCH ethtool v2 03/25] add --debug option to control debugging messages Michal Kubecek
@ 2020-03-04 20:24 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 05/25] netlink: add netlink related UAPI header files Michal Kubecek
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:24 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

The command line option list initialization does not look very nice and
adding another struct member would not make it look any better.

Reformat it to use named initializers. Also replace want_device with (bool)
no_dev as only two options have want_device = 0 so that using an opposite
would allow omitting the initializer for almost all options.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 ethtool.c | 559 ++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 358 insertions(+), 201 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index dd0242b27a2f..3186a72644fd 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5288,200 +5288,357 @@ 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;
+	int		(*func)(struct cmd_context *);
+	const char	*help;
+	const char	*xhelp;
+};
+
+static const struct option args[] = {
+	{
+		.opts	= "-s|--change",
+		.func	= do_sset,
+		.help	= "Change generic options",
+		.xhelp	= "		[ 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"
+	},
+	{
+		.opts	= "-a|--show-pause",
+		.func	= do_gpause,
+		.help	= "Show pause options"
+	},
+	{
+		.opts	= "-A|--pause",
+		.func	= do_spause,
+		.help	= "Set pause options",
+		.xhelp	= "		[ autoneg on|off ]\n"
+			  "		[ rx on|off ]\n"
+			  "		[ tx on|off ]\n"
+	},
+	{
+		.opts	= "-c|--show-coalesce",
+		.func	= do_gcoalesce,
+		.help	= "Show coalesce options"
+	},
+	{
+		.opts	= "-C|--coalesce",
+		.func	= do_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"
+	},
+	{
+		.opts	= "-g|--show-ring",
+		.func	= do_gring,
+		.help	= "Query RX/TX ring parameters"
+	},
+	{
+		.opts	= "-G|--set-ring",
+		.func	= do_sring,
+		.help	= "Set RX/TX ring parameters",
+		.xhelp	= "		[ rx N ]\n"
+			  "		[ rx-mini N ]\n"
+			  "		[ rx-jumbo N ]\n"
+			  "		[ tx N ]\n"
+	},
+	{
+		.opts	= "-k|--show-features|--show-offload",
+		.func	= do_gfeatures,
+		.help	= "Get state of protocol offload and other features"
+	},
+	{
+		.opts	= "-K|--features|--offload",
+		.func	= do_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",
+		.func	= do_gnicstats,
+		.help	= "Show adapter statistics"
+	},
+	{
+		.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,
+		.help	= "Show time stamping capabilities"
+	},
+	{
+		.opts	= "-x|--show-rxfh-indir|--show-rxfh",
+		.func	= do_grxfh,
+		.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,
+		.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,
+		.help	= "Query Channels"
+	},
+	{
+		.opts	= "-L|--set-channels",
+		.func	= do_schannels,
+		.help	= "Set Channels",
+		.xhelp	= "               [ rx N ]\n"
+			  "               [ tx N ]\n"
+			  "               [ other N ]\n"
+			  "               [ combined N ]\n"
+	},
+	{
+		.opts	= "--show-priv-flags",
+		.func	= do_gprivflags,
+		.help	= "Query private flags"
+	},
+	{
+		.opts	= "--set-priv-flags",
+		.func	= do_sprivflags,
+		.help	= "Set private flags",
+		.xhelp	= "		FLAG on|off ...\n"
+	},
+	{
+		.opts	= "-m|--dump-module-eeprom|--module-info",
+		.func	= do_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"
+	},
+	{
+		.opts	= "--show-eee",
+		.func	= do_geee,
+		.help	= "Show EEE settings",
+	},
+	{
+		.opts	= "--set-eee",
+		.func	= do_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	= "--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",
+		.func	= do_gfec,
+		.help	= "Show FEC settings",
+	},
+	{
+		.opts	= "--set-fec",
+		.func	= do_sfec,
+		.help	= "Set FEC settings",
+		.xhelp	= "		[ encoding auto|off|rs|baser [...]]\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	= "-h|--help",
+		.no_dev	= true,
+		.func	= show_usage,
+		.help	= "Show this help"
+	},
+	{
+		.opts	= "--version",
+		.no_dev	= true,
+		.func	= do_version,
+		.help	= "Show version number"
+	},
 	{}
 };
 
@@ -5499,10 +5656,10 @@ static int show_usage(struct cmd_context *ctx maybe_unused)
 		fputs("        ethtool [ --debug MASK ] ", 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);
 	}
 
 	return 0;
@@ -5702,8 +5859,8 @@ static int do_perqueue(struct cmd_context *ctx)
 int main(int argc, char **argp)
 {
 	int (*func)(struct cmd_context *);
-	int want_device;
 	struct cmd_context ctx;
+	bool no_dev;
 	int k;
 
 	init_global_link_mode_masks();
@@ -5738,16 +5895,16 @@ int main(int argc, char **argp)
 		argp++;
 		argc--;
 		func = args[k].func;
-		want_device = args[k].want_device;
+		no_dev = args[k].no_dev;
 		goto opt_found;
 	}
 	if ((*argp)[0] == '-')
 		exit_bad_args();
 	func = do_gset;
-	want_device = 1;
+	no_dev = false;
 
 opt_found:
-	if (want_device) {
+	if (!no_dev) {
 		ctx.devname = *argp++;
 		argc--;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 05/25] netlink: add netlink related UAPI header files
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (3 preceding siblings ...)
  2020-03-04 20:24 ` [PATCH ethtool v2 04/25] use named initializers in command line option list Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 06/25] netlink: introduce the netlink interface Michal Kubecek
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add copies of kernel UAPI header files needed for netlink interface:

  <linux/netlink.h>
  <linux/genetlink.h>
  <linux/ethtool_netlink.h>
  <linux/rtnetlink.h>
  <linux/if_link.h>

The (sanitized) copies are taken from v5.6-rc1.

v2:
  - add linux/rtnetlink.h and linux/if_link.h

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 uapi/linux/ethtool_netlink.h |  237 ++++++++
 uapi/linux/genetlink.h       |   89 +++
 uapi/linux/if_link.h         | 1051 ++++++++++++++++++++++++++++++++++
 uapi/linux/netlink.h         |  248 ++++++++
 uapi/linux/rtnetlink.h       |  777 +++++++++++++++++++++++++
 5 files changed, 2402 insertions(+)
 create mode 100644 uapi/linux/ethtool_netlink.h
 create mode 100644 uapi/linux/genetlink.h
 create mode 100644 uapi/linux/if_link.h
 create mode 100644 uapi/linux/netlink.h
 create mode 100644 uapi/linux/rtnetlink.h

diff --git a/uapi/linux/ethtool_netlink.h b/uapi/linux/ethtool_netlink.h
new file mode 100644
index 000000000000..ad6d3a00019d
--- /dev/null
+++ b/uapi/linux/ethtool_netlink.h
@@ -0,0 +1,237 @@
+/* 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.txt 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,
+
+	/* 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,
+
+	/* 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)
+
+#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \
+			  ETHTOOL_FLAG_OMIT_REPLY)
+
+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 */
+
+	/* 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 */
+
+	/* 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
+};
+
+/* 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 000000000000..1317119cbff8
--- /dev/null
+++ b/uapi/linux/genetlink.h
@@ -0,0 +1,89 @@
+/* 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_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_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)
+
+
+#endif /* __LINUX_GENERIC_NETLINK_H */
diff --git a/uapi/linux/if_link.h b/uapi/linux/if_link.h
new file mode 100644
index 000000000000..cb88bcb47287
--- /dev/null
+++ b/uapi/linux/if_link.h
@@ -0,0 +1,1051 @@
+/* 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;		/* total packets received	*/
+	__u32	tx_packets;		/* total packets transmitted	*/
+	__u32	rx_bytes;		/* total bytes received 	*/
+	__u32	tx_bytes;		/* total bytes transmitted	*/
+	__u32	rx_errors;		/* bad packets received		*/
+	__u32	tx_errors;		/* packet transmit problems	*/
+	__u32	rx_dropped;		/* no space in linux buffers	*/
+	__u32	tx_dropped;		/* no space available in linux	*/
+	__u32	multicast;		/* multicast packets received	*/
+	__u32	collisions;
+
+	/* detailed rx_errors: */
+	__u32	rx_length_errors;
+	__u32	rx_over_errors;		/* receiver ring buff overflow	*/
+	__u32	rx_crc_errors;		/* recved pkt with crc error	*/
+	__u32	rx_frame_errors;	/* recv'd frame alignment error */
+	__u32	rx_fifo_errors;		/* recv'r fifo overrun		*/
+	__u32	rx_missed_errors;	/* receiver missed packet	*/
+
+	/* 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;		/* dropped, no handler found	*/
+};
+
+/* The main device statistics structure */
+struct rtnl_link_stats64 {
+	__u64	rx_packets;		/* total packets received	*/
+	__u64	tx_packets;		/* total packets transmitted	*/
+	__u64	rx_bytes;		/* total bytes received 	*/
+	__u64	tx_bytes;		/* total bytes transmitted	*/
+	__u64	rx_errors;		/* bad packets received		*/
+	__u64	tx_errors;		/* packet transmit problems	*/
+	__u64	rx_dropped;		/* no space in linux buffers	*/
+	__u64	tx_dropped;		/* no space available in linux	*/
+	__u64	multicast;		/* multicast packets received	*/
+	__u64	collisions;
+
+	/* detailed rx_errors: */
+	__u64	rx_length_errors;
+	__u64	rx_over_errors;		/* receiver ring buff overflow	*/
+	__u64	rx_crc_errors;		/* recved pkt with crc error	*/
+	__u64	rx_frame_errors;	/* recv'd frame alignment error */
+	__u64	rx_fifo_errors;		/* recv'r fifo overrun		*/
+	__u64	rx_missed_errors;	/* receiver missed packet	*/
+
+	/* 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;		/* dropped, no handler found	*/
+};
+
+/* 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_MAX
+};
+
+
+#define IFLA_MAX (__IFLA_MAX - 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_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_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_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_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
+
+/* 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_MAX,
+};
+
+#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
+
+/* XFRM section */
+enum {
+	IFLA_XFRM_UNSPEC,
+	IFLA_XFRM_LINK,
+	IFLA_XFRM_IF_ID,
+	__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_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
+
+/* VXLAN section */
+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_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_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_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_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_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 section */
+
+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_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))
+
+/* 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_MAX
+};
+#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_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_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)
+
+/* 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_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)
+
+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;
+};
+
+#endif /* _LINUX_IF_LINK_H */
diff --git a/uapi/linux/netlink.h b/uapi/linux/netlink.h
new file mode 100644
index 000000000000..2c28d329e595
--- /dev/null
+++ b/uapi/linux/netlink.h
@@ -0,0 +1,248 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#include <linux/kernel.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_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 {
+	__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		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	/* Echo this request 		*/
+#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	*/
+
+/* 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_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;
+	/*
+	 * 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_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_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;
+};
+
+#endif /* __LINUX_NETLINK_H */
diff --git a/uapi/linux/rtnetlink.h b/uapi/linux/rtnetlink.h
new file mode 100644
index 000000000000..9d802cd7f695
--- /dev/null
+++ b/uapi/linux/rtnetlink.h
@@ -0,0 +1,777 @@
+/* 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_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_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_BABEL	42      /* Babel daemon */
+#define RTPROT_BGP	186     /* BGP Routes */
+#define RTPROT_ISIS	187     /* ISIS Routes */
+#define RTPROT_OSPF	188     /* OSPF Routes */
+#define RTPROT_RIP	189     /* RIP Routes */
+#define RTPROT_EIGRP	192     /* EIGRP Routes */
+
+/* rtm_scope
+
+   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 */
+
+/* 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	/* offloaded route */
+#define RTNH_F_LINKDOWN		16	/* carrier-down on nexthop */
+#define RTNH_F_UNRESOLVED	32	/* The entry is unresolved (ipmr) */
+
+#define RTNH_COMPARE_MASK	(RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
+
+/* 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[0];
+};
+
+/* 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_MAX
+};
+
+#define TCA_MAX (__TCA_MAX - 1)
+
+#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_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_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_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
+ *
+ */
+#define TCA_FLAG_LARGE_DUMP_ON		(1 << 0)
+
+/* 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)
+
+/* End of information exported to user level */
+
+
+
+#endif /* __LINUX_RTNETLINK_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 06/25] netlink: introduce the netlink interface
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (4 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 05/25] netlink: add netlink related UAPI header files Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 07/25] netlink: message buffer and composition helpers Michal Kubecek
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Initial part of netlink interface based on genetlink and libmnl. The
netlink interface is available in kernel since 5.6-rc1. This commit only
adds the generic infrastructure but does not override any ethtool command
so that there is no actual change in behaviour.

Netlink handlers for ethtool commands are added as nlfunc members to args
array in ethtool.c. A netlink handler is used if it is available (i.e.
nlfunc is not null) and ethtool succeeds to initialize netlink socket.
If netlink implementation exists for a command but the request is not
supported by kernel (e.g. when new ethtool is used on older kernel), ioctl
implementation is also used as fallback.

Running configure with --disable-netlink completely disables the netlink
interface.

v2:
  - change type of nl_context::suppress_nlerr
  - add nl_context::rtnl_socket

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       | 18 +++++++++--
 configure.ac      | 14 ++++++++-
 ethtool.c         | 76 +++++++++++++++++++++++++++++++++++------------
 internal.h        | 12 ++++++++
 netlink/extapi.h  | 31 +++++++++++++++++++
 netlink/netlink.c | 36 ++++++++++++++++++++++
 netlink/netlink.h | 26 ++++++++++++++++
 7 files changed, 191 insertions(+), 22 deletions(-)
 create mode 100644 netlink/extapi.h
 create mode 100644 netlink/netlink.c
 create mode 100644 netlink/netlink.h

diff --git a/Makefile.am b/Makefile.am
index 05beb22be669..b510c3ec8a03 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,8 @@ EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
 sbin_PROGRAMS = ethtool
 ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \
 		  uapi/linux/net_tstamp.h rxclass.c
+ethtool_CFLAGS = $(AM_CFLAGS)
+ethtool_LDADD = $(LDADD)
 if ETHTOOL_ENABLE_PRETTY_DUMP
 ethtool_SOURCES += \
 		  amd8111e.c de2104x.c dsa.c e100.c e1000.c et131x.c igb.c	\
@@ -22,12 +24,24 @@ bashcompletiondir = $(BASH_COMPLETION_DIR)
 dist_bashcompletion_DATA = shell-completion/bash/ethtool
 endif
 
+if ETHTOOL_ENABLE_NETLINK
+ethtool_SOURCES += \
+		  netlink/netlink.c netlink/netlink.h netlink/extapi.h \
+		  uapi/linux/ethtool_netlink.h \
+		  uapi/linux/netlink.h uapi/linux/genetlink.h \
+		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
+ethtool_CFLAGS += @MNL_CFLAGS@
+ethtool_LDADD += @MNL_LIBS@
+endif
+
 TESTS = test-cmdline test-features
 check_PROGRAMS = test-cmdline test-features
 test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES) 
-test_cmdline_CFLAGS = $(AM_FLAGS) -DTEST_ETHTOOL
+test_cmdline_CFLAGS = $(ethtool_CFLAGS) -DTEST_ETHTOOL
+test_cmdline_LDADD = $(ethtool_LDADD)
 test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES) 
-test_features_CFLAGS = $(AM_FLAGS) -DTEST_ETHTOOL
+test_features_CFLAGS = $(ethtool_CFLAGS) -DTEST_ETHTOOL
+test_features_LDADD = $(ethtool_LDADD)
 
 dist-hook:
 	cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/configure.ac b/configure.ac
index a5c1469f9b63..19e7fcb44fb5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script.
 AC_INIT(ethtool, 5.4, netdev@vger.kernel.org)
 AC_PREREQ(2.52)
 AC_CONFIG_SRCDIR([ethtool.c])
-AM_INIT_AUTOMAKE([gnu])
+AM_INIT_AUTOMAKE([gnu subdir-objects])
 AC_CONFIG_HEADERS([ethtool-config.h])
 
 AM_MAINTAINER_MODE
@@ -66,5 +66,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/ethtool.c b/ethtool.c
index 3186a72644fd..5d1ef537f692 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -48,6 +48,8 @@
 #include <linux/sockios.h>
 #include <linux/netlink.h>
 
+#include "netlink/extapi.h"
+
 #ifndef MAX_ADDR_LEN
 #define MAX_ADDR_LEN	32
 #endif
@@ -5292,6 +5294,7 @@ struct option {
 	const char	*opts;
 	bool		no_dev;
 	int		(*func)(struct cmd_context *);
+	int		(*nlfunc)(struct cmd_context *);
 	const char	*help;
 	const char	*xhelp;
 };
@@ -5856,11 +5859,36 @@ 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;
+	}
+
+	/* 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 (*nlfunc)(struct cmd_context *) = NULL;
 	int (*func)(struct cmd_context *);
-	struct cmd_context ctx;
+	struct cmd_context ctx = {};
 	bool no_dev;
+	int ret;
 	int k;
 
 	init_global_link_mode_masks();
@@ -5869,7 +5897,6 @@ int main(int argc, char **argp)
 	argp++;
 	argc--;
 
-	ctx.debug = 0;
 	if (*argp && !strcmp(*argp, "--debug")) {
 		char *eptr;
 
@@ -5895,6 +5922,7 @@ int main(int argc, char **argp)
 		argp++;
 		argc--;
 		func = args[k].func;
+		nlfunc = args[k].nlfunc;
 		no_dev = args[k].no_dev;
 		goto opt_found;
 	}
@@ -5904,33 +5932,43 @@ int main(int argc, char **argp)
 	no_dev = false;
 
 opt_found:
+	if (nlfunc) {
+		if (netlink_init(&ctx))
+			nlfunc = NULL;		/* fallback to ioctl() */
+	}
+
 	if (!no_dev) {
 		ctx.devname = *argp++;
 		argc--;
 
-		if (ctx.devname == NULL)
-			exit_bad_args();
-		if (strlen(ctx.devname) >= IFNAMSIZ)
+		/* netlink supports altnames, we will have to recheck against
+		 * IFNAMSIZ later in case of fallback to ioctl
+		 */
+		if (!ctx.devname || strlen(ctx.devname) >= ALTIFNAMSIZ) {
+			netlink_done(&ctx);
 			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;
 	}
 
 	ctx.argc = argc;
 	ctx.argp = argp;
 
+	if (nlfunc) {
+		ret = nlfunc(&ctx);
+		netlink_done(&ctx);
+		if ((ret != -EOPNOTSUPP) || !func)
+			return (ret >= 0) ? ret : 1;
+	}
+
+	if (ctx.devname && strlen(ctx.devname) >= IFNAMSIZ) {
+		fprintf(stderr,
+			"ethtool: device names longer than %u characters are only allowed with netlink\n",
+			IFNAMSIZ - 1);
+		exit_bad_args();
+	}
+	ret = ioctl_init(&ctx, no_dev);
+	if (ret)
+		return ret;
+
 	return func(&ctx);
 }
diff --git a/internal.h b/internal.h
index 9ec145f55dcb..72a04e638a13 100644
--- a/internal.h
+++ b/internal.h
@@ -25,6 +25,11 @@
 
 #define maybe_unused __attribute__((__unused__))
 
+/* internal for netlink interface */
+#ifdef ETHTOOL_ENABLE_NETLINK
+struct nl_context;
+#endif
+
 /* ethtool.h expects these to be defined by <linux/types.h> */
 #ifndef HAVE_BE_TYPES
 typedef uint16_t __be16;
@@ -44,6 +49,10 @@ typedef int32_t s32;
 #define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
 #endif
 
+#ifndef ALTIFNAMSIZ
+#define ALTIFNAMSIZ 128
+#endif
+
 #include <linux/ethtool.h>
 #include <linux/net_tstamp.h>
 
@@ -203,6 +212,9 @@ struct cmd_context {
 	int argc;		/* number of arguments to the sub-command */
 	char **argp;		/* arguments to the sub-command */
 	unsigned long debug;	/* debugging mask */
+#ifdef ETHTOOL_ENABLE_NETLINK
+	struct nl_context *nlctx;	/* netlink context (opaque) */
+#endif
 };
 
 #ifdef TEST_ETHTOOL
diff --git a/netlink/extapi.h b/netlink/extapi.h
new file mode 100644
index 000000000000..898dc6cfee71
--- /dev/null
+++ b/netlink/extapi.h
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+#ifdef ETHTOOL_ENABLE_NETLINK
+
+int netlink_init(struct cmd_context *ctx);
+void netlink_done(struct cmd_context *ctx);
+
+#else /* ETHTOOL_ENABLE_NETLINK */
+
+static inline int netlink_init(struct cmd_context *ctx maybe_unused)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void netlink_done(struct cmd_context *ctx maybe_unused)
+{
+}
+
+#endif /* ETHTOOL_ENABLE_NETLINK */
+
+#endif /* ETHTOOL_EXTAPI_H__ */
diff --git a/netlink/netlink.c b/netlink/netlink.c
new file mode 100644
index 000000000000..84e188119989
--- /dev/null
+++ b/netlink/netlink.c
@@ -0,0 +1,36 @@
+/*
+ * 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"
+
+/* initialization */
+
+int netlink_init(struct cmd_context *ctx)
+{
+	struct nl_context *nlctx;
+
+	nlctx = calloc(1, sizeof(*nlctx));
+	if (!nlctx)
+		return -ENOMEM;
+	nlctx->ctx = ctx;
+
+	ctx->nlctx = nlctx;
+
+	return 0;
+}
+
+void netlink_done(struct cmd_context *ctx)
+{
+	if (!ctx->nlctx)
+		return;
+
+	free(ctx->nlctx);
+	ctx->nlctx = NULL;
+}
diff --git a/netlink/netlink.h b/netlink/netlink.h
new file mode 100644
index 000000000000..99636ac8d9c4
--- /dev/null
+++ b/netlink/netlink.h
@@ -0,0 +1,26 @@
+/*
+ * 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>
+
+#define WILDCARD_DEVNAME "*"
+
+struct nl_context {
+	struct cmd_context	*ctx;
+	void			*cmd_private;
+	const char		*devname;
+	bool			is_dump;
+	int			exit_code;
+	unsigned int		suppress_nlerr;
+};
+
+#endif /* ETHTOOL_NETLINK_INT_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 07/25] netlink: message buffer and composition helpers
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (5 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 06/25] netlink: introduce the netlink interface Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 08/25] netlink: netlink socket wrapper and helpers Michal Kubecek
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add data structure for flexible message buffer and helpers for safe message
composition.

The nl_msg_buff structure is an abstraction for a message buffer used to
compose an outgoing netlink message. When the message exceeds currently
allocated length, buffer is reallocated. Only if the buffer size reaches
MAX_MSG_SIZE (4 MB), an error is issued.

v2:
  - add kerneldoc style comments

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       |   1 +
 netlink/msgbuff.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++
 netlink/msgbuff.h | 117 +++++++++++++++++++++
 netlink/netlink.h |   1 +
 4 files changed, 374 insertions(+)
 create mode 100644 netlink/msgbuff.c
 create mode 100644 netlink/msgbuff.h

diff --git a/Makefile.am b/Makefile.am
index b510c3ec8a03..81099a79a793 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,7 @@ endif
 if ETHTOOL_ENABLE_NETLINK
 ethtool_SOURCES += \
 		  netlink/netlink.c netlink/netlink.h netlink/extapi.h \
+		  netlink/msgbuff.c netlink/msgbuff.h \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/msgbuff.c b/netlink/msgbuff.c
new file mode 100644
index 000000000000..74065709ef7d
--- /dev/null
+++ b/netlink/msgbuff.c
@@ -0,0 +1,255 @@
+/*
+ * 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;
+
+	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 000000000000..24b99c5a28d7
--- /dev/null
+++ b/netlink/msgbuff.h
@@ -0,0 +1,117 @@
+/*
+ * 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_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.h b/netlink/netlink.h
index 99636ac8d9c4..1eaeec30b7d2 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -21,6 +21,7 @@ struct nl_context {
 	bool			is_dump;
 	int			exit_code;
 	unsigned int		suppress_nlerr;
+	uint16_t		ethnl_fam;
 };
 
 #endif /* ETHTOOL_NETLINK_INT_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 08/25] netlink: netlink socket wrapper and helpers
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (6 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 07/25] netlink: message buffer and composition helpers Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 09/25] netlink: initialize ethtool netlink socket Michal Kubecek
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add data structure for netlink socket and few helpers to work with it.

The nl_socket structure is a wrapper for struct mnl_socket from libmnl,
a message buffer and some additional data. Helpers for sending netlink
(and genetlink), processing replies are provided as well as debugging
helpers.

v2:
  - reworked debugging output for incoming/outgoing messages
  - drop tables of message type names (replaced in patch 22)
  - nl_context::suppress_nlerr allows two levels (EOPNOTSUPP / all codes)
  - drop nlsock_init(), rename __nlsock_init() to nlsock_init()
  - add kerneldoc style comments

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       |   3 +-
 internal.h        |   8 +
 netlink/netlink.c |  28 ++++
 netlink/netlink.h |  11 ++
 netlink/nlsock.c  | 364 ++++++++++++++++++++++++++++++++++++++++++++++
 netlink/nlsock.h  |  45 ++++++
 6 files changed, 458 insertions(+), 1 deletion(-)
 create mode 100644 netlink/nlsock.c
 create mode 100644 netlink/nlsock.h

diff --git a/Makefile.am b/Makefile.am
index 81099a79a793..04f4157e7bc0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,7 +27,8 @@ endif
 if ETHTOOL_ENABLE_NETLINK
 ethtool_SOURCES += \
 		  netlink/netlink.c netlink/netlink.h netlink/extapi.h \
-		  netlink/msgbuff.c netlink/msgbuff.h \
+		  netlink/msgbuff.c netlink/msgbuff.h netlink/nlsock.c \
+		  netlink/nlsock.h \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/internal.h b/internal.h
index 72a04e638a13..a518bfa99380 100644
--- a/internal.h
+++ b/internal.h
@@ -119,8 +119,16 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr)
 /* 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 */
 };
 
+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.
  */
diff --git a/netlink/netlink.c b/netlink/netlink.c
index 84e188119989..7d5eca666c84 100644
--- a/netlink/netlink.c
+++ b/netlink/netlink.c
@@ -10,6 +10,34 @@
 #include "netlink.h"
 #include "extapi.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)
+{
+	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;
+	int type = mnl_attr_get_type(attr);
+
+	if (type >= 0 && type <= tb_info->max_type)
+		tb_info->tb[type] = attr;
+
+	return MNL_CB_OK;
+}
+
 /* initialization */
 
 int netlink_init(struct cmd_context *ctx)
diff --git a/netlink/netlink.h b/netlink/netlink.h
index 1eaeec30b7d2..fbcea0b62240 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -24,4 +24,15 @@ struct nl_context {
 	uint16_t		ethnl_fam;
 };
 
+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);
+
 #endif /* ETHTOOL_NETLINK_INT_H__ */
diff --git a/netlink/nlsock.c b/netlink/nlsock.c
new file mode 100644
index 000000000000..d5fa668f508d
--- /dev/null
+++ b/netlink/nlsock.c
@@ -0,0 +1,364 @@
+/*
+ * 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"
+
+#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)
+{
+	if (nlhdr->nlmsg_type == ethnl_fam) {
+		const struct genlmsghdr *ghdr;
+
+		printf(" ethool");
+		if (nlhdr->nlmsg_len < NLMSG_HDRLEN + GENL_HDRLEN) {
+			printf(" malformed\n");
+			return;
+		}
+		ghdr = mnl_nlmsg_get_payload(nlhdr);
+
+		printf(" cmd %u", ghdr->cmd);
+		fputc('\n', stdout);
+
+		return;
+	}
+
+	if (nlhdr->nlmsg_type == GENL_ID_CTRL)
+		printf(" genl-ctrl\n");
+	else
+		fputc('\n', stdout);
+}
+
+static void rtnl_msg_summary(const struct nlmsghdr *nlhdr)
+{
+	unsigned int type = nlhdr->nlmsg_type;
+
+	printf(" type %u\n", type);
+}
+
+static void debug_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
+			      int nl_fam, bool outgoing)
+{
+	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);
+		break;
+	case NETLINK_ROUTE:
+		rtnl_msg_summary(nlhdr);
+		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;
+	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);
+	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);
+		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, ssize_t len,
+			      unsigned int suppress_nlerr)
+{
+	const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
+	DECLARE_ATTR_TB_INFO(tb);
+	unsigned int tlv_offset;
+	struct nlmsgerr *nlerr;
+	bool silent;
+
+	if (len < NLMSG_HDRLEN + sizeof(*nlerr))
+		return -EFAULT;
+	nlerr = mnl_nlmsg_get_payload(nlhdr);
+	silent = (!(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS) ||
+		  suppress_nlerr >= 2 ||
+		  (suppress_nlerr && nlerr->error == -EOPNOTSUPP));
+	if (silent)
+		goto out;
+
+	tlv_offset = sizeof(*nlerr);
+	if (!(nlhdr->nlmsg_flags & NLM_F_CAPPED))
+		tlv_offset += MNL_ALIGN(mnl_nlmsg_get_payload_len(&nlerr->msg));
+
+	if (mnl_attr_parse(nlhdr, tlv_offset, attr_cb, &tb_info) < 0)
+		goto out;
+	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 (tb[NLMSGERR_ATTR_OFFS])
+			fprintf(stderr, " (offset %u)",
+				mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]));
+		fputc('\n', stderr);
+	}
+
+out:
+	if (nlerr->error) {
+		errno = -nlerr->error;
+		if (!silent)
+			perror("netlink error");
+	}
+	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)
+			return nlsock_process_ack(nlhdr, len,
+						  nlsk->nlctx->suppress_nlerr);
+
+		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;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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->sk)
+		mnl_socket_close(nlsk->sk);
+	msgbuff_done(&nlsk->msgbuff);
+	memset(nlsk, '\0', sizeof(*nlsk));
+}
diff --git a/netlink/nlsock.h b/netlink/nlsock.h
new file mode 100644
index 000000000000..b015f8642704
--- /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__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 09/25] netlink: initialize ethtool netlink socket
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (7 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 08/25] netlink: netlink socket wrapper and helpers Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 10/25] netlink: add support for string sets Michal Kubecek
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

As part of the netlink initialization, set up netlink socket for ethtool
netlink and get id of ethtool genetlink family and monitor multicast group.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 netlink/netlink.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++-
 netlink/netlink.h | 11 ++++++
 2 files changed, 104 insertions(+), 1 deletion(-)

diff --git a/netlink/netlink.c b/netlink/netlink.c
index 7d5eca666c84..7cd7bef6eac9 100644
--- a/netlink/netlink.c
+++ b/netlink/netlink.c
@@ -9,6 +9,8 @@
 #include "../internal.h"
 #include "netlink.h"
 #include "extapi.h"
+#include "msgbuff.h"
+#include "nlsock.h"
 
 /* Used as reply callback for requests where no reply is expected (e.g. most
  * "set" type commands)
@@ -40,18 +42,108 @@ int attr_cb(const struct nlattr *attr, void *data)
 
 /* initialization */
 
+struct fam_info {
+	const char	*fam_name;
+	const char	*grp_name;
+	uint16_t	fam_id;
+	uint32_t	grp_id;
+};
+
+static void find_mc_group(struct nlattr *nest, struct fam_info *info)
+{
+	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]),
+			   info->grp_name))
+			continue;
+		info->grp_id =
+			mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]);
+		return;
+	}
+}
+
+static int family_info_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+	struct fam_info *info = data;
+	struct nlattr *attr;
+
+	mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
+		switch (mnl_attr_get_type(attr)) {
+		case CTRL_ATTR_FAMILY_ID:
+			info->fam_id = mnl_attr_get_u16(attr);
+			break;
+		case CTRL_ATTR_MCAST_GROUPS:
+			find_mc_group(attr, info);
+			break;
+		}
+	}
+
+	return MNL_CB_OK;
+}
+
+static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info)
+{
+	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+	int ret;
+
+	nlsk->nlctx->suppress_nlerr = 2;
+	ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
+			 NLM_F_REQUEST | NLM_F_ACK, 1);
+	if (ret < 0)
+		goto out;
+	ret = -EMSGSIZE;
+	if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, info->fam_name))
+		goto out;
+
+	nlsock_sendmsg(nlsk, NULL);
+	nlsock_process_reply(nlsk, family_info_cb, info);
+	ret = info->fam_id ? 0 : -EADDRNOTAVAIL;
+
+out:
+	nlsk->nlctx->suppress_nlerr = 0;
+	return ret;
+}
+
 int netlink_init(struct cmd_context *ctx)
 {
+	struct fam_info info = {
+		.fam_name	= ETHTOOL_GENL_NAME,
+		.grp_name	= ETHTOOL_MCGRP_MONITOR_NAME,
+	};
 	struct nl_context *nlctx;
+	int ret;
 
 	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->ethnl_socket, &info);
+	if (ret < 0)
+		goto out_nlsk;
+	nlctx->ethnl_fam = info.fam_id;
+	nlctx->ethnl_mongrp = info.grp_id;
 
 	ctx->nlctx = nlctx;
-
 	return 0;
+
+out_nlsk:
+	nlsock_done(nlctx->ethnl_socket);
+out_free:
+	free(nlctx);
+	return ret;
 }
 
 void netlink_done(struct cmd_context *ctx)
diff --git a/netlink/netlink.h b/netlink/netlink.h
index fbcea0b62240..9ba03b05163f 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -11,6 +11,7 @@
 #include <linux/netlink.h>
 #include <linux/genetlink.h>
 #include <linux/ethtool_netlink.h>
+#include "nlsock.h"
 
 #define WILDCARD_DEVNAME "*"
 
@@ -22,6 +23,9 @@ struct nl_context {
 	int			exit_code;
 	unsigned int		suppress_nlerr;
 	uint16_t		ethnl_fam;
+	uint32_t		ethnl_mongrp;
+	struct nl_socket	*ethnl_socket;
+	struct nl_socket	*ethnl2_socket;
 };
 
 struct attr_tb_info {
@@ -35,4 +39,11 @@ struct attr_tb_info {
 int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data);
 int attr_cb(const struct nlattr *attr, void *data);
 
+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);
+}
+
 #endif /* ETHTOOL_NETLINK_INT_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 10/25] netlink: add support for string sets
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (8 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 09/25] netlink: initialize ethtool netlink socket Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 11/25] netlink: add notification monitor Michal Kubecek
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add infrastructure for querying kernel for string sets (analog to ioctl
commands ETHTOOL_GSSET_INFO and ETHTOOL_GSTRINGS), caching the results and
making them available to netlink code.

There are two types of string sets: global (not related to a device) and
per device (each device has its set of string sets). Per device string sets
are stored in a linked list (one entry for each device) for now.

String sets can be either preloaded completely on start using
preload_global_strings() and preload_perdev_strings() or requested by one
when there is a need for them. In the latter case (preferred, in particular
for one shot operation mode), second netlink socket is used to request the
string set contents.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       |   2 +-
 netlink/netlink.c |  53 +++++++++
 netlink/netlink.h |   9 ++
 netlink/strset.c  | 295 ++++++++++++++++++++++++++++++++++++++++++++++
 netlink/strset.h  |  25 ++++
 5 files changed, 383 insertions(+), 1 deletion(-)
 create mode 100644 netlink/strset.c
 create mode 100644 netlink/strset.h

diff --git a/Makefile.am b/Makefile.am
index 04f4157e7bc0..aa41b21fa779 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,7 +28,7 @@ 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/nlsock.h netlink/strset.c netlink/strset.h \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/netlink.c b/netlink/netlink.c
index 7cd7bef6eac9..60f7912181df 100644
--- a/netlink/netlink.c
+++ b/netlink/netlink.c
@@ -11,6 +11,7 @@
 #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)
@@ -40,6 +41,57 @@ int attr_cb(const struct nlattr *attr, void *data)
 	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;
+}
+
 /* initialization */
 
 struct fam_info {
@@ -153,4 +205,5 @@ void netlink_done(struct cmd_context *ctx)
 
 	free(ctx->nlctx);
 	ctx->nlctx = NULL;
+	cleanup_all_strings();
 }
diff --git a/netlink/netlink.h b/netlink/netlink.h
index 9ba03b05163f..04ad7bcd347c 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -39,6 +39,15 @@ struct attr_tb_info {
 int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data);
 int attr_cb(const struct nlattr *attr, void *data);
 
+const char *get_dev_name(const struct nlattr *nest);
+int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname);
+
+static inline void copy_devname(char *dst, const char *src)
+{
+	strncpy(dst, src, ALTIFNAMSIZ);
+	dst[ALTIFNAMSIZ - 1] = '\0';
+}
+
 static inline int netlink_init_ethnl2_socket(struct nl_context *nlctx)
 {
 	if (nlctx->ethnl2_socket)
diff --git a/netlink/strset.c b/netlink/strset.c
new file mode 100644
index 000000000000..bc468ae5a88e
--- /dev/null
+++ b/netlink/strset.c
@@ -0,0 +1,295 @@
+/*
+ * 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;
+		nlctx->devname = devname;
+	}
+
+	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 000000000000..72a4a3929244
--- /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__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 11/25] netlink: add notification monitor
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (9 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 10/25] netlink: add support for string sets Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 12/25] move shared code into a common file Michal Kubecek
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

With 'ethtool --monitor [ --all | opt ] [dev]', let ethtool listen to
netlink notification and display them in a format similar to output of
related "get" commands.

With --all or without option, show all types of notifications. If a device
name is specified, show only notifications for that device, if no device
name or "*" is passed, show notifications for all devices.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       |   1 +
 ethtool.8.in      |  21 +++++
 ethtool.c         |  14 ++++
 netlink/extapi.h  |   8 ++
 netlink/monitor.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++
 netlink/netlink.h |  11 +++
 netlink/strset.c  |   2 +
 7 files changed, 254 insertions(+)
 create mode 100644 netlink/monitor.c

diff --git a/Makefile.am b/Makefile.am
index aa41b21fa779..0aa88ead27d5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,6 +29,7 @@ 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 \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/ethtool.8.in b/ethtool.8.in
index 680cad9fbb8f..28e4f75eee8d 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -133,6 +133,13 @@ ethtool \- query or control network driver and hardware settings
 .BN --debug
 .I args
 .HP
+.B ethtool \-\-monitor
+[
+.I command
+] [
+.I devname
+]
+.HP
 .B ethtool \-a|\-\-show\-pause
 .I devname
 .HP
@@ -1244,6 +1251,20 @@ 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 \-\-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
 .SH BUGS
 Not supported (in part or whole) on all network drivers.
 .SH AUTHOR
diff --git a/ethtool.c b/ethtool.c
index 5d1ef537f692..97eaa58a3090 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5664,6 +5664,7 @@ static int show_usage(struct cmd_context *ctx maybe_unused)
 		if (args[i].xhelp)
 			fputs(args[i].xhelp, stdout);
 	}
+	nl_monitor_usage();
 
 	return 0;
 }
@@ -5909,6 +5910,19 @@ int main(int argc, char **argp)
 		argp += 2;
 		argc -= 2;
 	}
+#ifdef ETHTOOL_ENABLE_NETLINK
+	if (*argp && !strcmp(*argp, "--monitor")) {
+		if (netlink_init(&ctx)) {
+			fprintf(stderr,
+				"Option --monitor is only available with netlink.\n");
+			return 1;
+		} else {
+			ctx.argp = ++argp;
+			ctx.argc = --argc;
+			return nl_monitor(&ctx);
+		}
+	}
+#endif
 
 	/* First argument must be either a valid option or a device
 	 * name to get settings for (which we don't expect to begin
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 898dc6cfee71..1d5b68226af4 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -15,6 +15,10 @@ struct nl_context;
 int netlink_init(struct cmd_context *ctx);
 void netlink_done(struct cmd_context *ctx);
 
+int nl_monitor(struct cmd_context *ctx);
+
+void nl_monitor_usage(void);
+
 #else /* ETHTOOL_ENABLE_NETLINK */
 
 static inline int netlink_init(struct cmd_context *ctx maybe_unused)
@@ -26,6 +30,10 @@ static inline void netlink_done(struct cmd_context *ctx maybe_unused)
 {
 }
 
+static inline void nl_monitor_usage(void)
+{
+}
+
 #endif /* ETHTOOL_ENABLE_NETLINK */
 
 #endif /* ETHTOOL_EXTAPI_H__ */
diff --git a/netlink/monitor.c b/netlink/monitor.c
new file mode 100644
index 000000000000..300fd5bc2e51
--- /dev/null
+++ b/netlink/monitor.c
@@ -0,0 +1,197 @@
+/*
+ * 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[] = {
+};
+
+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,
+	},
+};
+
+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 = ctx->nlctx;
+	struct nl_socket *nlsk = nlctx->ethnl_socket;
+	uint32_t grpid = nlctx->ethnl_mongrp;
+	bool is_dev;
+	int ret;
+
+	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++) {
+		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/netlink.h b/netlink/netlink.h
index 04ad7bcd347c..be44f9c16f19 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -14,6 +14,7 @@
 #include "nlsock.h"
 
 #define WILDCARD_DEVNAME "*"
+#define CMDMASK_WORDS DIV_ROUND_UP(__ETHTOOL_MSG_KERNEL_CNT, 32)
 
 struct nl_context {
 	struct cmd_context	*ctx;
@@ -26,6 +27,9 @@ struct nl_context {
 	uint32_t		ethnl_mongrp;
 	struct nl_socket	*ethnl_socket;
 	struct nl_socket	*ethnl2_socket;
+	bool			is_monitor;
+	uint32_t		filter_cmds[CMDMASK_WORDS];
+	const char		*filter_devname;
 };
 
 struct attr_tb_info {
@@ -48,6 +52,13 @@ static inline void copy_devname(char *dst, const char *src)
 	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)
diff --git a/netlink/strset.c b/netlink/strset.c
index bc468ae5a88e..75f0327bbe43 100644
--- a/netlink/strset.c
+++ b/netlink/strset.c
@@ -149,6 +149,8 @@ static int strset_reply_cb(const struct nlmsghdr *nlhdr, void *data)
 			return MNL_CB_OK;
 		nlctx->devname = devname;
 	}
+	if (ifindex && !dev_ok(nlctx))
+		return MNL_CB_OK;
 
 	if (ifindex) {
 		struct perdev_strings *perdev;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 12/25] move shared code into a common file
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (10 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 11/25] netlink: add notification monitor Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 13/25] netlink: add bitset helpers Michal Kubecek
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Move code which is going to be shared between ioctl and netlink
implementation into a common file common.c and declarations into header
file common.h.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am |   2 +-
 common.c    | 145 +++++++++++++++++++++++++++++++++++++++++++++++++
 common.h    |  26 +++++++++
 ethtool.c   | 151 +++-------------------------------------------------
 4 files changed, 179 insertions(+), 145 deletions(-)
 create mode 100644 common.c
 create mode 100644 common.h

diff --git a/Makefile.am b/Makefile.am
index 0aa88ead27d5..90723a49b591 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
 
 sbin_PROGRAMS = ethtool
 ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \
-		  uapi/linux/net_tstamp.h rxclass.c
+		  uapi/linux/net_tstamp.h rxclass.c common.c common.h
 ethtool_CFLAGS = $(AM_CFLAGS)
 ethtool_LDADD = $(LDADD)
 if ETHTOOL_ENABLE_PRETTY_DUMP
diff --git a/common.c b/common.c
new file mode 100644
index 000000000000..f9c41a32d3a3
--- /dev/null
+++ b/common.c
@@ -0,0 +1,145 @@
+/*
+ * 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;
+
+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");
+	}
+}
diff --git a/common.h b/common.h
new file mode 100644
index 000000000000..3a680114a7c2
--- /dev/null
+++ b/common.h
@@ -0,0 +1,26 @@
+/*
+ * 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"
+
+#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;
+
+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);
+
+#endif /* ETHTOOL_COMMON_H__ */
diff --git a/ethtool.c b/ethtool.c
index 97eaa58a3090..c2b7cc8c0502 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -48,32 +48,13 @@
 #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
@@ -121,29 +102,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;
@@ -426,26 +384,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;
@@ -904,31 +842,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;
 }
@@ -1000,58 +916,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;
@@ -2839,8 +2703,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) {
@@ -2886,13 +2749,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)];
+	struct cmdline_info cmdline_msglvl[n_flags_msglvl];
 	int argc = ctx->argc;
 	char **argp = ctx->argp;
 	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,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 13/25] netlink: add bitset helpers
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (11 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 12/25] move shared code into a common file Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 14/25] netlink: partial netlink handler for gset (no option) Michal Kubecek
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add basic functions for work with arbitrary length bitsets used in ethtool
netlink interface.

The most important function is walk_bitset() which iterates through bits of
a bitset (passed as pointer to the nest attribute) and calls provided
callback for each bit.

v2:
  - add bitset_is_compact() helper

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am      |   2 +-
 netlink/bitset.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++
 netlink/bitset.h |  26 ++++++
 3 files changed, 245 insertions(+), 1 deletion(-)
 create mode 100644 netlink/bitset.c
 create mode 100644 netlink/bitset.h

diff --git a/Makefile.am b/Makefile.am
index 90723a49b591..2553cf727a36 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,7 +29,7 @@ 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/monitor.c netlink/bitset.c netlink/bitset.h \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/bitset.c b/netlink/bitset.c
new file mode 100644
index 000000000000..ed109ec1d2c0
--- /dev/null
+++ b/netlink/bitset.c
@@ -0,0 +1,218 @@
+/*
+ * 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;
+	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);
+
+		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 || 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;
+}
+
+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 000000000000..4b587d2c8a04
--- /dev/null
+++ b/netlink/bitset.h
@@ -0,0 +1,26 @@
+/*
+ * 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);
+int walk_bitset(const struct nlattr *bitset, const struct stringset *labels,
+		bitset_walk_callback cb, void *data);
+
+#endif /* ETHTOOL_NETLINK_BITSET_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 14/25] netlink: partial netlink handler for gset (no option)
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (12 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 13/25] netlink: add bitset helpers Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 15/25] netlink: support getting wake-on-lan and debugging settings Michal Kubecek
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Partially implement "ethtool <dev>" subcommand. This commit retrieves and
displays information provided by ETHTOOL_MSG_LINKINFO_GET,
ETHTOOL_MSG_LINKMODES_GET and ETHTOOL_MSG_LINKSTATE_GET netlink requests,
i.e. everything except wake-on-lan and debugging (msglevel).

Also register the callbacks in monitor so that corresponding notifications
can be displayed with "ethtool --monitor".

v2:
  - print banner only once unless monitor or wildcard (dump request)
  - suppress error messages for -EOPNOTSUPP responses
  - only show "No data available" if there is no reply at all
  - more readable nl_gset()

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am        |   1 +
 ethtool.c          |   1 +
 netlink/extapi.h   |   3 +
 netlink/monitor.c  |  16 ++
 netlink/netlink.h  |   4 +
 netlink/settings.c | 647 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 672 insertions(+)
 create mode 100644 netlink/settings.c

diff --git a/Makefile.am b/Makefile.am
index 2553cf727a36..77003f27a3a8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,6 +30,7 @@ ethtool_SOURCES += \
 		  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 \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/ethtool.c b/ethtool.c
index c2b7cc8c0502..a69233bd73fc 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5805,6 +5805,7 @@ int main(int argc, char **argp)
 	}
 	if ((*argp)[0] == '-')
 		exit_bad_args();
+	nlfunc = nl_gset;
 	func = do_gset;
 	no_dev = false;
 
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 1d5b68226af4..8608ea7f51f5 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -15,6 +15,7 @@ struct nl_context;
 int netlink_init(struct cmd_context *ctx);
 void netlink_done(struct cmd_context *ctx);
 
+int nl_gset(struct cmd_context *ctx);
 int nl_monitor(struct cmd_context *ctx);
 
 void nl_monitor_usage(void);
@@ -34,6 +35,8 @@ static inline void nl_monitor_usage(void)
 {
 }
 
+#define nl_gset			NULL
+
 #endif /* ETHTOOL_ENABLE_NETLINK */
 
 #endif /* ETHTOOL_EXTAPI_H__ */
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 300fd5bc2e51..e8fdcd2b93ef 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -15,6 +15,14 @@ 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,
+	},
 };
 
 static void clear_filter(struct nl_context *nlctx)
@@ -70,6 +78,14 @@ 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,
+	},
 };
 
 static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/netlink.h b/netlink/netlink.h
index be44f9c16f19..16754899976d 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -30,6 +30,7 @@ struct nl_context {
 	bool			is_monitor;
 	uint32_t		filter_cmds[CMDMASK_WORDS];
 	const char		*filter_devname;
+	bool			no_banner;
 };
 
 struct attr_tb_info {
@@ -46,6 +47,9 @@ int attr_cb(const struct nlattr *attr, void *data);
 const char *get_dev_name(const struct nlattr *nest);
 int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname);
 
+int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+
 static inline void copy_devname(char *dst, const char *src)
 {
 	strncpy(dst, src, ALTIFNAMSIZ);
diff --git a/netlink/settings.c b/netlink/settings.c
new file mode 100644
index 000000000000..46c292a3d92d
--- /dev/null
+++ b/netlink/settings.c
@@ -0,0 +1,647 @@
+/*
+ * settings.c - netlink implementation of settings commands
+ *
+ * Implementation of "ethtool <dev>" and "ethtool -s <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"
+
+/* GET_SETTINGS */
+
+enum link_mode_class {
+	LM_CLASS_UNKNOWN,
+	LM_CLASS_REAL,
+	LM_CLASS_AUTONEG,
+	LM_CLASS_PORT,
+	LM_CLASS_PAUSE,
+	LM_CLASS_FEC,
+};
+
+struct link_mode_info {
+	enum link_mode_class	class;
+	u32			speed;
+	u8			duplex;
+};
+
+static const char *const names_duplex[] = {
+	[DUPLEX_HALF]		= "Half",
+	[DUPLEX_FULL]		= "Full",
+};
+
+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
+ */
+static const struct link_mode_info link_modes[] = {
+	[ETHTOOL_LINK_MODE_10baseT_Half_BIT] =
+		{ LM_CLASS_REAL,	10,	DUPLEX_HALF },
+	[ETHTOOL_LINK_MODE_10baseT_Full_BIT] =
+		{ LM_CLASS_REAL,	10,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100baseT_Half_BIT] =
+		{ LM_CLASS_REAL,	100,	DUPLEX_HALF },
+	[ETHTOOL_LINK_MODE_100baseT_Full_BIT] =
+		{ LM_CLASS_REAL,	100,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_1000baseT_Half_BIT] =
+		{ LM_CLASS_REAL,	1000,	DUPLEX_HALF },
+	[ETHTOOL_LINK_MODE_1000baseT_Full_BIT] =
+		{ LM_CLASS_REAL,	1000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_Autoneg_BIT] =
+		{ LM_CLASS_AUTONEG },
+	[ETHTOOL_LINK_MODE_TP_BIT] =
+		{ LM_CLASS_PORT },
+	[ETHTOOL_LINK_MODE_AUI_BIT] =
+		{ LM_CLASS_PORT },
+	[ETHTOOL_LINK_MODE_MII_BIT] =
+		{ LM_CLASS_PORT },
+	[ETHTOOL_LINK_MODE_FIBRE_BIT] =
+		{ LM_CLASS_PORT },
+	[ETHTOOL_LINK_MODE_BNC_BIT] =
+		{ LM_CLASS_PORT },
+	[ETHTOOL_LINK_MODE_10000baseT_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_Pause_BIT] =
+		{ LM_CLASS_PAUSE },
+	[ETHTOOL_LINK_MODE_Asym_Pause_BIT] =
+		{ LM_CLASS_PAUSE },
+	[ETHTOOL_LINK_MODE_2500baseX_Full_BIT] =
+		{ LM_CLASS_REAL,	2500,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_Backplane_BIT] =
+		{ LM_CLASS_PORT },
+	[ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] =
+		{ LM_CLASS_REAL,	1000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] =
+		{ LM_CLASS_REAL,	20000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] =
+		{ LM_CLASS_REAL,	20000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] =
+		{ LM_CLASS_REAL,	40000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] =
+		{ LM_CLASS_REAL,	40000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] =
+		{ LM_CLASS_REAL,	40000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] =
+		{ LM_CLASS_REAL,	40000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] =
+		{ LM_CLASS_REAL,	56000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] =
+		{ LM_CLASS_REAL,	56000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] =
+		{ LM_CLASS_REAL,	56000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] =
+		{ LM_CLASS_REAL,	56000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] =
+		{ LM_CLASS_REAL,	25000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] =
+		{ LM_CLASS_REAL,	25000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] =
+		{ LM_CLASS_REAL,	25000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_1000baseX_Full_BIT] =
+		{ LM_CLASS_REAL,	1000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_10000baseER_Full_BIT] =
+		{ LM_CLASS_REAL,	10000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_2500baseT_Full_BIT] =
+		{ LM_CLASS_REAL,	2500,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_5000baseT_Full_BIT] =
+		{ LM_CLASS_REAL,	5000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_FEC_NONE_BIT] =
+		{ LM_CLASS_FEC },
+	[ETHTOOL_LINK_MODE_FEC_RS_BIT] =
+		{ LM_CLASS_FEC },
+	[ETHTOOL_LINK_MODE_FEC_BASER_BIT] =
+		{ LM_CLASS_FEC },
+	[ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] =
+		{ LM_CLASS_REAL,	50000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] =
+		{ LM_CLASS_REAL,	100000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] =
+		{ LM_CLASS_REAL,	200000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] =
+		{ LM_CLASS_REAL,	200000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] =
+		{ LM_CLASS_REAL,	200000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] =
+		{ LM_CLASS_REAL,	200000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] =
+		{ LM_CLASS_REAL,	200000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_100baseT1_Full_BIT] =
+		{ LM_CLASS_REAL,	100,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] =
+		{ LM_CLASS_REAL,	1000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] =
+		{ LM_CLASS_REAL,	400000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] =
+		{ LM_CLASS_REAL,	400000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] =
+		{ LM_CLASS_REAL,	400000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] =
+		{ LM_CLASS_REAL,	400000,	DUPLEX_FULL },
+	[ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] =
+		{ LM_CLASS_REAL,	400000,	DUPLEX_FULL },
+};
+const unsigned int link_modes_count = ARRAY_SIZE(link_modes);
+
+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;
+}
+
+static int dump_link_modes(struct nl_context *nlctx,
+			   const struct nlattr *bitset, bool mask,
+			   unsigned int class, const char *before,
+			   const char *between, const char *after,
+			   const char *if_none)
+{
+	const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
+	DECLARE_ATTR_TB_INFO(bitset_tb);
+	const unsigned int before_len = strlen(before);
+	const struct nlattr *bits;
+	const struct nlattr *bit;
+	bool first = true;
+	int prev = -2;
+	int ret;
+
+	ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+	bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
+	if (ret < 0)
+		goto err_nonl;
+	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[10];
+
+			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 && (prev == idx - 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 && !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) && (prev == idx - 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(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, true, 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, true, LM_CLASS_FEC,
+			      "Link partner advertised FEC modes: ",
+			      " ", "\n", "No");
+	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_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");
+	}
+
+	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] &&
+	    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;
+}
+
+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\n", val ? "yes" : "no");
+	}
+
+	return MNL_CB_OK;
+}
+
+static int gset_request(struct nl_socket *nlsk, uint8_t msg_type,
+			uint16_t hdr_attr, mnl_cb_t cb)
+{
+	int ret;
+
+	ret = nlsock_prep_get_request(nlsk, msg_type, hdr_attr, 0);
+	if (ret < 0)
+		return ret;
+	return nlsock_send_get_request(nlsk, cb);
+}
+
+int nl_gset(struct cmd_context *ctx)
+{
+	struct nl_context *nlctx = ctx->nlctx;
+	struct nl_socket *nlsk = nlctx->ethnl_socket;
+	int ret;
+
+	nlctx->suppress_nlerr = 1;
+
+	ret = gset_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,
+			   ETHTOOL_A_LINKMODES_HEADER, linkmodes_reply_cb);
+	if (ret == -ENODEV)
+		return ret;
+
+	ret = gset_request(nlsk, ETHTOOL_MSG_LINKINFO_GET,
+			   ETHTOOL_A_LINKINFO_HEADER, linkinfo_reply_cb);
+	if (ret == -ENODEV)
+		return ret;
+
+	ret = gset_request(nlsk, ETHTOOL_MSG_LINKSTATE_GET,
+			   ETHTOOL_A_LINKSTATE_HEADER, linkstate_reply_cb);
+	if (ret == -ENODEV)
+		return ret;
+
+	if (!nlctx->no_banner) {
+		printf("No data available\n");
+		return 75;
+	}
+
+	return 0;
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 15/25] netlink: support getting wake-on-lan and debugging settings
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (13 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 14/25] netlink: partial netlink handler for gset (no option) Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:25 ` [PATCH ethtool v2 16/25] netlink: add basic command line parsing helpers Michal Kubecek
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Finish "ethtool <dev>" command implementation by adding support for
wake-on-lan (ETHTOOL_MSG_WOL_GET) and debugging (ETHTOOL_MSG_DEBUG_GET,
currently only msglevel) settings.

Register the callbacks also with monitor so that "ethtool --monitor" can
display corresponding notifications.

v2:
  - suppress error messages for -EOPNOTSUPP responses
  - perform WOL_GET and DEBUG_GET requests before LINKSTATE_GET
  - adjust to changes in patch 14

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 netlink/monitor.c  |  16 +++++++
 netlink/netlink.h  |   2 +
 netlink/settings.c | 115 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)

diff --git a/netlink/monitor.c b/netlink/monitor.c
index e8fdcd2b93ef..5fce6b64c08c 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -23,6 +23,14 @@ static struct {
 		.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,
+	},
 };
 
 static void clear_filter(struct nl_context *nlctx)
@@ -86,6 +94,14 @@ static struct monitor_option monitor_opts[] = {
 		.pattern	= "-s|--change",
 		.cmd		= ETHTOOL_MSG_LINKMODES_NTF,
 	},
+	{
+		.pattern	= "-s|--change",
+		.cmd		= ETHTOOL_MSG_WOL_NTF,
+	},
+	{
+		.pattern	= "-s|--change",
+		.cmd		= ETHTOOL_MSG_DEBUG_NTF,
+	},
 };
 
 static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/netlink.h b/netlink/netlink.h
index 16754899976d..730f8e1b3fe9 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -49,6 +49,8 @@ int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname);
 
 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);
 
 static inline void copy_devname(char *dst, const char *src)
 {
diff --git a/netlink/settings.c b/netlink/settings.c
index 46c292a3d92d..e95bbcc9ad86 100644
--- a/netlink/settings.c
+++ b/netlink/settings.c
@@ -604,6 +604,110 @@ int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data)
 	return MNL_CB_OK;
 }
 
+void wol_modes_cb(unsigned int idx, const char *name, 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, bool val, void *data)
+{
+	u32 *msg_mask = data;
+
+	if (idx >= 32)
+		return;
+	if (val)
+		*msg_mask |= (1U << idx);
+}
+
+void msgmask_cb2(unsigned int idx, const char *name, bool val, void *data)
+{
+	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;
+}
+
 static int gset_request(struct nl_socket *nlsk, uint8_t msg_type,
 			uint16_t hdr_attr, mnl_cb_t cb)
 {
@@ -633,6 +737,16 @@ int nl_gset(struct cmd_context *ctx)
 	if (ret == -ENODEV)
 		return ret;
 
+	ret = gset_request(nlsk, ETHTOOL_MSG_WOL_GET, ETHTOOL_A_WOL_HEADER,
+			   wol_reply_cb);
+	if (ret == -ENODEV)
+		return ret;
+
+	ret = gset_request(nlsk, ETHTOOL_MSG_DEBUG_GET, ETHTOOL_A_DEBUG_HEADER,
+			   debug_reply_cb);
+	if (ret == -ENODEV)
+		return ret;
+
 	ret = gset_request(nlsk, ETHTOOL_MSG_LINKSTATE_GET,
 			   ETHTOOL_A_LINKSTATE_HEADER, linkstate_reply_cb);
 	if (ret == -ENODEV)
@@ -643,5 +757,6 @@ int nl_gset(struct cmd_context *ctx)
 		return 75;
 	}
 
+
 	return 0;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 16/25] netlink: add basic command line parsing helpers
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (14 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 15/25] netlink: support getting wake-on-lan and debugging settings Michal Kubecek
@ 2020-03-04 20:25 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 17/25] netlink: add bitset command line parser handlers Michal Kubecek
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:25 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Existing command line parser functions from the ioctl interface are often
too closely tied to the ioctl data structures and would not be easily
adapted to generating netlink messages. Introduce a new parser framework
which assigns parser handlers to parameters and provide basic handlers for
most common parameter types:

  - flag represented by presence of a parameter
  - u8 bool represented by name followed by "on" or "off"
  - string represented by string argument
  - u32 represented by numeric argument
  - u8 represented by numeric argument
  - u32 represented by symbolic name (lookup table)
  - u8 represented by symbolic name (lookup table)
  - binary blob represented by MAC-like syntax

Main parser entry point is nl_parse() function; it gets an array of with
entries assigning parser handlers and optional data to command line
parameters and generates corresponding netlink attributes or fills raw
data.

Optionally, attributes can be divided into multiple netlink messages or
multiple nested attributes.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       |   2 +-
 netlink/netlink.h |   4 +
 netlink/parser.c  | 615 ++++++++++++++++++++++++++++++++++++++++++++++
 netlink/parser.h  | 123 ++++++++++
 4 files changed, 743 insertions(+), 1 deletion(-)
 create mode 100644 netlink/parser.c
 create mode 100644 netlink/parser.h

diff --git a/Makefile.am b/Makefile.am
index 77003f27a3a8..2df64cc4cc52 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,7 +30,7 @@ ethtool_SOURCES += \
 		  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/settings.c netlink/parser.c netlink/parser.h \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/netlink.h b/netlink/netlink.h
index 730f8e1b3fe9..3ab5f2329e2f 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -31,6 +31,10 @@ struct nl_context {
 	uint32_t		filter_cmds[CMDMASK_WORDS];
 	const char		*filter_devname;
 	bool			no_banner;
+	const char		*cmd;
+	const char		*param;
+	char			**argp;
+	int			argc;
 };
 
 struct attr_tb_info {
diff --git a/netlink/parser.c b/netlink/parser.c
new file mode 100644
index 000000000000..0e2190eed0b4
--- /dev/null
+++ b/netlink/parser.c
@@ -0,0 +1,615 @@
+/*
+ * 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 bool __prefix_0x(const char *p)
+{
+	return p[0] == '0' && (p[1] == 'x' || p[1] == 'X');
+}
+
+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, uint16_t type, const void *data,
+		  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,
+		    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, 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, 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 (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,
+		    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;
+}
+
+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, const void *data,
+		   struct nl_msg_buff *msgbuff, void *dest)
+{
+	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;
+}
+
+/* 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));
+}
+
+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;
+	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;
+		msgbuff_done(&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
+ */
+int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+	      void *dest, enum parser_group_style group_style)
+{
+	struct nl_socket *nlsk = nlctx->ethnl_socket;
+	const struct param_parser *parser;
+	struct tmp_buff *buffs = NULL;
+	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;
+
+		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:
+			ret = msg_init(nlctx, msgbuff, parser->group,
+				       NLM_F_REQUEST | NLM_F_ACK);
+			if (ret < 0)
+				goto out_free_buffs;
+			if (ethnla_fill_header(msgbuff,
+					       ETHTOOL_A_LINKINFO_HEADER,
+					       nlctx->devname, 0))
+				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;
+		}
+		__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 ? (dest + parser->dest_offset) : NULL;
+		ret = parser->handler(nlctx, parser->type, parser->handler_data,
+				      msgbuff, param_dest);
+		if (ret < 0)
+			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:
+			ret = nlsock_sendmsg(nlsk, msgbuff);
+			if (ret < 0)
+				goto out_free;
+			ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL);
+			if (ret < 0)
+				goto out_free;
+			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 000000000000..b5e8cd418b50
--- /dev/null
+++ b/netlink/parser.h
@@ -0,0 +1,123 @@
+/*
+ * 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;
+};
+
+/* 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;
+};
+
+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_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);
+
+/* 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);
+
+#endif /* ETHTOOL_NETLINK_PARSER_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 17/25] netlink: add bitset command line parser handlers
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (15 preceding siblings ...)
  2020-03-04 20:25 ` [PATCH ethtool v2 16/25] netlink: add basic command line parsing helpers Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 18/25] netlink: add netlink handler for sset (-s) Michal Kubecek
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add three more command line parser handlers for different representation of
bitsets:

  - series of "name on|off" pairs for bitset with mask
  - series of names for bitsets without mask (lists)
  - string consisting of characters representing bits (e.g. WoL modes);
    extended syntax "([+-][a-z])+" is supported for bitsets with mask

Numeric syntax %u[/%u] is also supported.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 netlink/parser.c | 443 +++++++++++++++++++++++++++++++++++++++++++++++
 netlink/parser.h |  21 +++
 2 files changed, 464 insertions(+)

diff --git a/netlink/parser.c b/netlink/parser.c
index 0e2190eed0b4..40eb4a5c0b26 100644
--- a/netlink/parser.c
+++ b/netlink/parser.c
@@ -43,6 +43,12 @@ static void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
 		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');
@@ -282,6 +288,35 @@ int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
 	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))
@@ -405,6 +440,414 @@ int nl_parse_error(struct nl_context *nlctx, uint16_t type, const void *data,
 	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 ? (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)
+		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,
diff --git a/netlink/parser.h b/netlink/parser.h
index b5e8cd418b50..3cc26d21916c 100644
--- a/netlink/parser.h
+++ b/netlink/parser.h
@@ -79,6 +79,20 @@ struct error_parser_data {
 	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 */
@@ -115,6 +129,13 @@ int nl_parse_error(struct nl_context *nlctx, uint16_t type, const void *data,
 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,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 18/25] netlink: add netlink handler for sset (-s)
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (16 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 17/25] netlink: add bitset command line parser handlers Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 19/25] netlink: support tests with netlink enabled Michal Kubecek
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Implement "ethtool -s <dev>" subcommand using netlink interface request
ETHTOOL_MSG_LINKINFO_SET, ETHTOOL_MSG_LINKMODES_SET, ETHTOOL_MSG_WOL_SET
and ETHTOOL_MSG_DEBUG_SET.

Parser grouping with PARSER_GROUP_MSG group style is used to create
multiple request messages from one set of attributes which can be in
arbitrary order.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 ethtool.8.in       |  15 ++--
 ethtool.c          |   9 ++-
 netlink/extapi.h   |   2 +
 netlink/settings.c | 193 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 210 insertions(+), 9 deletions(-)

diff --git a/ethtool.8.in b/ethtool.8.in
index 28e4f75eee8d..ba85cfe4f413 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -242,16 +242,21 @@ ethtool \- query or control network driver and hardware settings
 .I devname
 .BN speed
 .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 [ msglvl
-.IR N \ |
+.IR N\fP[/\fIM\fP] \ |
 .BI msglvl \ type
 .A1 on off
 .RB ...]
@@ -638,7 +643,7 @@ with just the device name as an argument will show you the supported device spee
 .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
 .A3 mdix auto on off
diff --git a/ethtool.c b/ethtool.c
index a69233bd73fc..baa6458bb486 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5166,18 +5166,19 @@ static const struct option args[] = {
 	{
 		.opts	= "-s|--change",
 		.func	= do_sset,
+		.nlfunc	= nl_sset,
 		.help	= "Change generic options",
 		.xhelp	= "		[ speed %d ]\n"
 			  "		[ duplex half|full ]\n"
-			  "		[ port tp|aui|bnc|mii|fibre ]\n"
+			  "		[ port tp|aui|bnc|mii|fibre|da ]\n"
 			  "		[ mdix auto|on|off ]\n"
 			  "		[ autoneg on|off ]\n"
-			  "		[ advertise %x ]\n"
+			  "		[ advertise %x[/%x] | mode on|off ... [--] ]\n"
 			  "		[ phyad %d ]\n"
 			  "		[ xcvr internal|external ]\n"
-			  "		[ wol p|u|m|b|a|g|s|f|d... ]\n"
+			  "		[ wol %d[/%d] | 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"
+			  "		[ msglvl %d[/%d] | type on|off ... [--] ]\n"
 	},
 	{
 		.opts	= "-a|--show-pause",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 8608ea7f51f5..612002e8228b 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -16,6 +16,7 @@ int netlink_init(struct cmd_context *ctx);
 void netlink_done(struct cmd_context *ctx);
 
 int nl_gset(struct cmd_context *ctx);
+int nl_sset(struct cmd_context *ctx);
 int nl_monitor(struct cmd_context *ctx);
 
 void nl_monitor_usage(void);
@@ -36,6 +37,7 @@ static inline void nl_monitor_usage(void)
 }
 
 #define nl_gset			NULL
+#define nl_sset			NULL
 
 #endif /* ETHTOOL_ENABLE_NETLINK */
 
diff --git a/netlink/settings.c b/netlink/settings.c
index e95bbcc9ad86..07eeddc87557 100644
--- a/netlink/settings.c
+++ b/netlink/settings.c
@@ -13,6 +13,7 @@
 #include "netlink.h"
 #include "strset.h"
 #include "bitset.h"
+#include "parser.h"
 
 /* GET_SETTINGS */
 
@@ -760,3 +761,195 @@ int nl_gset(struct cmd_context *ctx)
 
 	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_u32 duplex_values[] = {
+	{ .arg = "half",	.val = DUPLEX_HALF },
+	{ .arg = "full",	.val = DUPLEX_FULL },
+	{}
+};
+
+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		= "duplex",
+		.group		= ETHTOOL_MSG_LINKMODES_SET,
+		.type		= ETHTOOL_A_LINKMODES_DUPLEX,
+		.handler	= nl_parse_lookup_u8,
+		.handler_data	= duplex_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,
+	},
+	{}
+};
+
+int nl_sset(struct cmd_context *ctx)
+{
+	struct nl_context *nlctx = ctx->nlctx;
+	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);
+	if (ret < 0)
+		return 1;
+
+	if (ret == 0)
+		return 0;
+	return nlctx->exit_code ?: 75;
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 19/25] netlink: support tests with netlink enabled
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (17 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 18/25] netlink: add netlink handler for sset (-s) Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 20/25] netlink: add handler for permaddr (-P) Michal Kubecek
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

As there is no netlink implementation of features (-k / -K) yet, we only
need to take care of test-cmdline. With this patch, the full test suite
succeeds with both --enable-netlink and --disable-netlink.

There are two differences between results with netlink enabled and
disabled:

1. The netlink interface allows network device names up to 127 characters
(ALTIFNAMSIZ - 1). While alternative names can be used to identify a device
with ioctl, fixed structure does not allow passing names longer than 15
characters (IFNAMSIZ - 1).

2. Failure with deprecated 'xcvr' parameter for -s / --change is done in
slightly different way so that netlink code will have it interpreted as
a parser failure while ioctl code as (fake) request failure. Another
difference is that no kernel with ethtool netlink support can possibly
allow changing transceiver using ethtool request.

The command line tests affected by these differences have expected return
code depending on ETHTOOL_ENABLE_NETLINK.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 netlink/netlink.c |  7 +++++++
 netlink/nlsock.c  |  2 ++
 test-cmdline.c    | 29 ++++++++++++++++++++++++++---
 test-features.c   | 11 +++++++++++
 4 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/netlink/netlink.c b/netlink/netlink.c
index 60f7912181df..39f406387233 100644
--- a/netlink/netlink.c
+++ b/netlink/netlink.c
@@ -143,6 +143,12 @@ static int family_info_cb(const struct nlmsghdr *nlhdr, void *data)
 	return MNL_CB_OK;
 }
 
+#ifdef TEST_ETHTOOL
+static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info)
+{
+	return 0;
+}
+#else
 static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info)
 {
 	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
@@ -165,6 +171,7 @@ out:
 	nlsk->nlctx->suppress_nlerr = 0;
 	return ret;
 }
+#endif
 
 int netlink_init(struct cmd_context *ctx)
 {
diff --git a/netlink/nlsock.c b/netlink/nlsock.c
index d5fa668f508d..4366c52ce390 100644
--- a/netlink/nlsock.c
+++ b/netlink/nlsock.c
@@ -261,6 +261,7 @@ int nlsock_prep_get_request(struct nl_socket *nlsk, unsigned int nlcmd,
 	return 0;
 }
 
+#ifndef TEST_ETHTOOL
 /**
  * nlsock_sendmsg() - send a netlink message to kernel
  * @nlsk:    netlink socket
@@ -277,6 +278,7 @@ ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *altbuff)
 	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
diff --git a/test-cmdline.c b/test-cmdline.c
index b76e2c3e640b..a6c0bd1efc2d 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" },
@@ -294,6 +306,17 @@ int send_ioctl(struct cmd_context *ctx, void *cmd)
 	test_exit(0);
 }
 
+#ifdef ETHTOOL_ENABLE_NETLINK
+struct nl_socket;
+struct nl_msg_buff;
+
+ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *altbuff)
+{
+	/* If we get this far then parsing succeeded */
+	test_exit(0);
+}
+#endif
+
 int main(void)
 {
 	struct test_case *tc;
diff --git a/test-features.c b/test-features.c
index 6ebb364803a2..b9f80f073d1f 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;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 20/25] netlink: add handler for permaddr (-P)
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (18 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 19/25] netlink: support tests with netlink enabled Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 21/25] netlink: support for pretty printing netlink messages Michal Kubecek
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Implement "ethtool -P <dev>" subcommand. This retrieves and displays
information traditionally provided by ETHTOOL_GPERMADDR ioctl request.

Permanent hardware address is provided by rtnetlink RTM_GETLINK request
so that basic support for rtnetlink (which is not based on genetlink)
requests is also added.

The IFLA_PERM_ADDRESS attribute in RTM_NEWLINK message is not set if the
device driver did not set its permanent hardware address which is hard to
distinguish from ethtool running on top of an older kernel not supporting
the attribute. As IFLA_PERM_ADDRESS attribute was added to kernel shortly
before the initial portion of ethtool netlink interface, we let ethtool
initialize ethtool netlink even if it is not needed so that we can fall
back to ioctl on older kernels and assume that absence of IFLA_PERM_ADDRESS
means the permanent address is not set. This would be only wrong on some
distribution kernel which would backport ethtool netlink interface but not
IFLA_PERM_ADDRESS which is unlikely.

Permanent address is immutable (or not mutable using standard interfaces)
so that there is no related notification message.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am        |   1 +
 ethtool.c          |   1 +
 netlink/extapi.h   |   2 +
 netlink/netlink.h  |   8 ++++
 netlink/permaddr.c | 114 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 126 insertions(+)
 create mode 100644 netlink/permaddr.c

diff --git a/Makefile.am b/Makefile.am
index 2df64cc4cc52..38268932a955 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,6 +31,7 @@ ethtool_SOURCES += \
 		  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 \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/ethtool.c b/ethtool.c
index baa6458bb486..1b4e08b6e60f 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5372,6 +5372,7 @@ static const struct option args[] = {
 	{
 		.opts	= "-P|--show-permaddr",
 		.func	= do_permaddr,
+		.nlfunc	= nl_permaddr,
 		.help	= "Show permanent hardware address"
 	},
 	{
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 612002e8228b..d5b3cd92d4ab 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -17,6 +17,7 @@ void netlink_done(struct cmd_context *ctx);
 
 int nl_gset(struct cmd_context *ctx);
 int nl_sset(struct cmd_context *ctx);
+int nl_permaddr(struct cmd_context *ctx);
 int nl_monitor(struct cmd_context *ctx);
 
 void nl_monitor_usage(void);
@@ -38,6 +39,7 @@ static inline void nl_monitor_usage(void)
 
 #define nl_gset			NULL
 #define nl_sset			NULL
+#define nl_permaddr		NULL
 
 #endif /* ETHTOOL_ENABLE_NETLINK */
 
diff --git a/netlink/netlink.h b/netlink/netlink.h
index 3ab5f2329e2f..db078d28fabb 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -27,6 +27,7 @@ struct nl_context {
 	uint32_t		ethnl_mongrp;
 	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;
@@ -76,4 +77,11 @@ static inline int netlink_init_ethnl2_socket(struct nl_context *nlctx)
 	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/permaddr.c b/netlink/permaddr.c
new file mode 100644
index 000000000000..006eac6c0094
--- /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);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 21/25] netlink: support for pretty printing netlink messages
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (19 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 20/25] netlink: add handler for permaddr (-P) Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 22/25] netlink: message format description for ethtool netlink Michal Kubecek
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

To improve message reporting and debugging, add support for displaying
netlink messages in human readable form, e.g.

    # ethtool --debug 0x10 -s eth0 msglvl drv on foo on probe off
    netlink error: bit name not found
    offending message and attribute:
        ETHTOOL_MSG_DEBUG_SET
            ETHTOOL_A_DEBUG_HEADER
                ETHTOOL_A_HEADER_DEV_NAME = "eth0"
            ETHTOOL_A_DEBUG_MSGMASK
                ETHTOOL_A_BITSET_BITS
                    ETHTOOL_A_BITSET_BITS_BIT
                        ETHTOOL_A_BITSET_BIT_NAME = "drv"
                        ETHTOOL_A_BITSET_BIT_VALUE = true
                    ETHTOOL_A_BITSET_BITS_BIT
    ===>                ETHTOOL_A_BITSET_BIT_NAME = "foo"
                        ETHTOOL_A_BITSET_BIT_VALUE = true
                    ETHTOOL_A_BITSET_BITS_BIT
                        ETHTOOL_A_BITSET_BIT_NAME = "probe"

This commit only adds support for parsing and displaying a message and
(optionally) highlighting an attribute on given offset (for extack error
reporting). To actually use it, one also needs message descriptions, i.e.
mapping of netlink attribute types to their symbolic names and payload
formats (depending on context).

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am         |   2 +-
 netlink/prettymsg.c | 193 ++++++++++++++++++++++++++++++++++++++++++++
 netlink/prettymsg.h | 102 +++++++++++++++++++++++
 3 files changed, 296 insertions(+), 1 deletion(-)
 create mode 100644 netlink/prettymsg.c
 create mode 100644 netlink/prettymsg.h

diff --git a/Makefile.am b/Makefile.am
index 38268932a955..baef0d20fa41 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,7 +31,7 @@ ethtool_SOURCES += \
 		  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/permaddr.c netlink/prettymsg.c netlink/prettymsg.h \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/prettymsg.c b/netlink/prettymsg.c
new file mode 100644
index 000000000000..74fe6f2db7ed
--- /dev/null
+++ b/netlink/prettymsg.c
@@ -0,0 +1,193 @@
+/*
+ * 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 <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_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_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_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;
+	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);
+}
diff --git a/netlink/prettymsg.h b/netlink/prettymsg.h
new file mode 100644
index 000000000000..68ec275a22f6
--- /dev/null
+++ b/netlink/prettymsg.h
@@ -0,0 +1,102 @@
+/*
+ * 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_X8,
+	NLA_X16,
+	NLA_X32,
+	NLA_S8,
+	NLA_S16,
+	NLA_S32,
+	NLA_STRING,
+	NLA_FLAG,
+	NLA_BOOL,
+	NLA_NESTED,
+	NLA_ARRAY,
+};
+
+struct pretty_nla_desc {
+	enum pretty_nla_format		format;
+	const char			*name;
+	const struct pretty_nla_desc	*children;
+	unsigned int			n_children;
+};
+
+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_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_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_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 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);
+
+#endif /* ETHTOOL_NETLINK_PRETTYMSG_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 22/25] netlink: message format description for ethtool netlink
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (20 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 21/25] netlink: support for pretty printing netlink messages Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 23/25] netlink: message format descriptions for genetlink control Michal Kubecek
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add description of ethtool netlink message formats to be used for pretty
printing infrastructure. These arrays map (numeric) attribute types to
their symbolic names and format of their payload so that attributes can be
displayed in human friendly form.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am            |   1 +
 netlink/desc-ethtool.c | 139 +++++++++++++++++++++++++++++++++++++++++
 netlink/prettymsg.h    |   7 +++
 3 files changed, 147 insertions(+)
 create mode 100644 netlink/desc-ethtool.c

diff --git a/Makefile.am b/Makefile.am
index baef0d20fa41..adbe5bc52b4b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,6 +32,7 @@ ethtool_SOURCES += \
 		  netlink/monitor.c netlink/bitset.c netlink/bitset.h \
 		  netlink/settings.c netlink/parser.c netlink/parser.h \
 		  netlink/permaddr.c netlink/prettymsg.c netlink/prettymsg.h \
+		  netlink/desc-ethtool.c \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c
new file mode 100644
index 000000000000..76c6f13e4648
--- /dev/null
+++ b/netlink/desc-ethtool.c
@@ -0,0 +1,139 @@
+/*
+ * desc-ethtool.c - ethtool netlink format descriptions
+ *
+ * Descriptions of ethtool netlink messages and attributes for pretty print.
+ */
+
+#include <linux/ethtool_netlink.h>
+
+#include "../internal.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 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),
+};
+
+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),
+};
+
+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),
+};
+
+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),
+};
+
+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),
+};
+
+const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc);
diff --git a/netlink/prettymsg.h b/netlink/prettymsg.h
index 68ec275a22f6..9d8ca77fd42c 100644
--- a/netlink/prettymsg.h
+++ b/netlink/prettymsg.h
@@ -99,4 +99,11 @@ int pretty_print_genlmsg(const struct nlmsghdr *nlhdr,
 			 const struct pretty_nlmsg_desc *desc,
 			 unsigned int ndesc, 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;
+
 #endif /* ETHTOOL_NETLINK_PRETTYMSG_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 23/25] netlink: message format descriptions for genetlink control
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (21 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 22/25] netlink: message format description for ethtool netlink Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 24/25] netlink: message format descriptions for rtnetlink Michal Kubecek
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

For the sake of completeness, add description of genetlink message formats
of GENL_ID_CTRL family. We use this to get the id of "ethtool" genetlink
family and "monitor" multicast group.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am             |  2 +-
 netlink/desc-genlctrl.c | 56 +++++++++++++++++++++++++++++++++++++++++
 netlink/prettymsg.h     |  3 +++
 3 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 netlink/desc-genlctrl.c

diff --git a/Makefile.am b/Makefile.am
index adbe5bc52b4b..74143b727c4f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,7 +32,7 @@ ethtool_SOURCES += \
 		  netlink/monitor.c netlink/bitset.c netlink/bitset.h \
 		  netlink/settings.c netlink/parser.c netlink/parser.h \
 		  netlink/permaddr.c netlink/prettymsg.c netlink/prettymsg.h \
-		  netlink/desc-ethtool.c \
+		  netlink/desc-ethtool.c netlink/desc-genlctrl.c \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/desc-genlctrl.c b/netlink/desc-genlctrl.c
new file mode 100644
index 000000000000..9840179b0a1a
--- /dev/null
+++ b/netlink/desc-genlctrl.c
@@ -0,0 +1,56 @@
+/*
+ * 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 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),
+};
+
+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),
+};
+
+const unsigned int genlctrl_msg_n_desc = ARRAY_SIZE(genlctrl_msg_desc);
diff --git a/netlink/prettymsg.h b/netlink/prettymsg.h
index 9d8ca77fd42c..50d0bbf43665 100644
--- a/netlink/prettymsg.h
+++ b/netlink/prettymsg.h
@@ -106,4 +106,7 @@ 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;
+
 #endif /* ETHTOOL_NETLINK_PRETTYMSG_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 24/25] netlink: message format descriptions for rtnetlink
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (22 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 23/25] netlink: message format descriptions for genetlink control Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:26 ` [PATCH ethtool v2 25/25] netlink: use pretty printing for ethtool netlink messages Michal Kubecek
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Add description of rtnetlink message formats to be used for pretty printing
infrastructure and helper function to pretty print rtnetlink messages.

As rtnetlink is quite complex and only RTM_GETLINK and RTM_NEWLINK messages
are interesting for ethtool at the moment, this commit provides only names
of a limited subset of rtnetlink messages and names and formats of top
level attributes used in RTM_{NEW,DEL,GET,SET}LINK messages.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am         |  1 +
 netlink/desc-rtnl.c | 96 +++++++++++++++++++++++++++++++++++++++++++++
 netlink/prettymsg.c | 44 +++++++++++++++++++++
 netlink/prettymsg.h |  6 +++
 4 files changed, 147 insertions(+)
 create mode 100644 netlink/desc-rtnl.c

diff --git a/Makefile.am b/Makefile.am
index 74143b727c4f..2fd79eb8c79a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,6 +33,7 @@ ethtool_SOURCES += \
 		  netlink/settings.c netlink/parser.c netlink/parser.h \
 		  netlink/permaddr.c netlink/prettymsg.c netlink/prettymsg.h \
 		  netlink/desc-ethtool.c netlink/desc-genlctrl.c \
+		  netlink/desc-rtnl.c \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
 		  uapi/linux/rtnetlink.h uapi/linux/if_link.h
diff --git a/netlink/desc-rtnl.c b/netlink/desc-rtnl.c
new file mode 100644
index 000000000000..e15e6f0f0d2e
--- /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/prettymsg.c b/netlink/prettymsg.c
index 74fe6f2db7ed..9e62bebe615e 100644
--- a/netlink/prettymsg.c
+++ b/netlink/prettymsg.c
@@ -191,3 +191,47 @@ int pretty_print_genlmsg(const struct nlmsghdr *nlhdr,
 				  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_flags)
+		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
index 50d0bbf43665..b5e5f735ac8a 100644
--- a/netlink/prettymsg.h
+++ b/netlink/prettymsg.h
@@ -98,6 +98,7 @@ struct pretty_nlmsg_desc {
 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 */
 
@@ -109,4 +110,9 @@ 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__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH ethtool v2 25/25] netlink: use pretty printing for ethtool netlink messages
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (23 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 24/25] netlink: message format descriptions for rtnetlink Michal Kubecek
@ 2020-03-04 20:26 ` Michal Kubecek
  2020-03-04 20:34 ` [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
  2020-03-05 19:24 ` John W. Linville
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:26 UTC (permalink / raw)
  To: John Linville, netdev; +Cc: Andrew Lunn, Florian Fainelli

Introduce new debugging flag DEBUG_NL_PRETTY_MSG (0x10) which has two
effects:

  - show request messages embedded in extack error messages in human
    readable form; if failing attribute offset is provided, highlight the
    attribute
  - if DEBUG_NL_MSGS is also set, show structure of all outgoing and
    incoming ethtool netlink messages in addition to their summary

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 internal.h       |  1 +
 netlink/nlsock.c | 71 +++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 56 insertions(+), 16 deletions(-)

diff --git a/internal.h b/internal.h
index a518bfa99380..edb07bddd073 100644
--- a/internal.h
+++ b/internal.h
@@ -122,6 +122,7 @@ enum {
 	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)
diff --git a/netlink/nlsock.c b/netlink/nlsock.c
index 4366c52ce390..22abb68b6646 100644
--- a/netlink/nlsock.c
+++ b/netlink/nlsock.c
@@ -10,6 +10,7 @@
 #include "../internal.h"
 #include "nlsock.h"
 #include "netlink.h"
+#include "prettymsg.h"
 
 #define NLSOCK_RECV_BUFFSIZE 65536
 
@@ -43,10 +44,12 @@ static void ctrl_msg_summary(const struct nlmsghdr *nlhdr)
 }
 
 static void genl_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
-			     bool outgoing)
+			     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) {
@@ -55,27 +58,46 @@ static void genl_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
 		}
 		ghdr = mnl_nlmsg_get_payload(nlhdr);
 
-		printf(" cmd %u", ghdr->cmd);
+		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)
+	if (nlhdr->nlmsg_type == GENL_ID_CTRL) {
 		printf(" genl-ctrl\n");
-	else
+		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)
+static void rtnl_msg_summary(const struct nlmsghdr *nlhdr, bool pretty)
 {
 	unsigned int type = nlhdr->nlmsg_type;
 
-	printf(" type %u\n", 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)
+			      int nl_fam, bool outgoing, bool pretty)
 {
 	printf("    msg length %u", nlhdr->nlmsg_len);
 
@@ -86,10 +108,10 @@ static void debug_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
 
 	switch(nl_fam) {
 	case NETLINK_GENERIC:
-		genl_msg_summary(nlhdr, ethnl_fam, outgoing);
+		genl_msg_summary(nlhdr, ethnl_fam, outgoing, pretty);
 		break;
 	case NETLINK_ROUTE:
-		rtnl_msg_summary(nlhdr);
+		rtnl_msg_summary(nlhdr, pretty);
 		break;
 	default:
 		fputc('\n', stdout);
@@ -103,13 +125,14 @@ static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len,
 	const char *dirlabel = outgoing ? "sending" : "received";
 	uint32_t debug = nlsk->nlctx->ctx->debug;
 	const struct nlmsghdr *nlhdr = msg;
-	bool summary, dump;
+	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) {
@@ -128,7 +151,7 @@ static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len,
 	while (nlhdr && left > 0 && mnl_nlmsg_ok(nlhdr, left)) {
 		if (summary)
 			debug_msg_summary(nlhdr, nlsk->nlctx->ethnl_fam,
-					  nlsk->nl_fam, outgoing);
+					  nlsk->nl_fam, outgoing, pretty);
 		if (dump)
 			mnl_nlmsg_fprintf(stdout, nlhdr, nlhdr->nlmsg_len,
 					  GENL_HDRLEN);
@@ -146,7 +169,7 @@ static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len,
  * Return: error code extracted from the message
  */
 static int nlsock_process_ack(struct nlmsghdr *nlhdr, ssize_t len,
-			      unsigned int suppress_nlerr)
+			      unsigned int suppress_nlerr, bool pretty)
 {
 	const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
 	DECLARE_ATTR_TB_INFO(tb);
@@ -174,12 +197,23 @@ static int nlsock_process_ack(struct nlmsghdr *nlhdr, ssize_t len,
 
 		fprintf(stderr, "netlink %s: %s",
 			nlerr->error ? "error" : "warning", msg);
-		if (tb[NLMSGERR_ATTR_OFFS])
+		if (!pretty && tb[NLMSGERR_ATTR_OFFS])
 			fprintf(stderr, " (offset %u)",
 				mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]));
 		fputc('\n', stderr);
 	}
 
+	if (nlerr->error && pretty) {
+		unsigned int err_offset = 0;
+
+		if (tb[NLMSGERR_ATTR_OFFS])
+			err_offset = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
+		fprintf(stderr, "offending message%s:\n",
+			err_offset ? " and attribute" : "");
+		pretty_print_genlmsg(&nlerr->msg, ethnl_umsg_desc,
+				     ethnl_umsg_n_desc, err_offset);
+	}
+
 out:
 	if (nlerr->error) {
 		errno = -nlerr->error;
@@ -223,9 +257,14 @@ int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data)
 			return -EFAULT;
 
 		nlhdr = (struct nlmsghdr *)buff;
-		if (nlhdr->nlmsg_type == NLMSG_ERROR)
-			return nlsock_process_ack(nlhdr, len,
-						  nlsk->nlctx->suppress_nlerr);
+		if (nlhdr->nlmsg_type == NLMSG_ERROR) {
+			bool silent = nlsk->nlctx->suppress_nlerr;
+			bool pretty;
+
+			pretty = debug_on(nlsk->nlctx->ctx->debug,
+					  DEBUG_NL_PRETTY_MSG);
+			return nlsock_process_ack(nlhdr, len, silent, pretty);
+		}
 
 		msgbuff->nlhdr = nlhdr;
 		msgbuff->genlhdr = mnl_nlmsg_get_payload(nlhdr);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (24 preceding siblings ...)
  2020-03-04 20:26 ` [PATCH ethtool v2 25/25] netlink: use pretty printing for ethtool netlink messages Michal Kubecek
@ 2020-03-04 20:34 ` Michal Kubecek
  2020-03-05 19:24 ` John W. Linville
  26 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-04 20:34 UTC (permalink / raw)
  To: John Linville; +Cc: netdev, Andrew Lunn, Florian Fainelli

John,

If you would prefer that, feel free to use v1 and I'll send the later
updates as a shorter follow-up series.

Michal

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release
  2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
                   ` (25 preceding siblings ...)
  2020-03-04 20:34 ` [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
@ 2020-03-05 19:24 ` John W. Linville
  2020-03-05 20:32   ` Michal Kubecek
  26 siblings, 1 reply; 30+ messages in thread
From: John W. Linville @ 2020-03-05 19:24 UTC (permalink / raw)
  To: Michal Kubecek; +Cc: netdev, Andrew Lunn, Florian Fainelli

[-- Attachment #1: Type: text/plain, Size: 6975 bytes --]

On Wed, Mar 04, 2020 at 09:24:35PM +0100, Michal Kubecek wrote:
> This series adds initial support for ethtool netlink interface provided by
> kernel since 5.6-rc1. The traditional ioctl interface is still supported
> for compatibility with older kernels. The netlink interface and message
> formats are documented in Documentation/networking/ethtool-netlink.rst file
> in kernel source tree.
> 
> Netlink interface is preferred but ethtool falls back to ioctl if netlink
> interface is not available (i.e. the "ethtool" genetlink family is not
> registered). It also falls back if a particular command is not implemented
> in netlink (kernel returns -EOPNOTSUPP). This allows new ethtool versions
> to work with older kernel versions while support for ethool commands is
> added in steps.
> 
> The series aims to touch existing ioctl code as little as possible in the
> first phase to minimize the risk of introducing regressions. It is also
> possible to build ethtool without netlink support if --disable-netlink is
> passed to configure script. The most visible changes to existing code are
> 
>   - UAPI header copies are moved to uapi/ under original names
>   - some variables and functions which are going to be shared with netlink
>     code are moved from ethtool.c to common.c and common.h
>   - args[] array in ethtool.c was rewritten to use named initializers
> 
> Except for changes to main(), all netlink specific code is in a separate
> directory netlink/ and is divided into multiple files.
> 
> Changes in v2:
> - add support for permanent hardware addres ("ethtool -P", patch 20)
> - add support for pretty printing of netlink messages (patches 21-25)
> - make output of "ethtool <dev>" closer to ioctl implementation
> - two more kernel uapi header copies (patch 5)
> - support for rtnetlink socket and requests (needed for "ethtool -P")
> - some kerneldoc style comments
> 
> Michal Kubecek (25):
>   move UAPI header copies to a separate directory
>   update UAPI header copies
>   add --debug option to control debugging messages
>   use named initializers in command line option list
>   netlink: add netlink related UAPI header files
>   netlink: introduce the netlink interface
>   netlink: message buffer and composition helpers
>   netlink: netlink socket wrapper and helpers
>   netlink: initialize ethtool netlink socket
>   netlink: add support for string sets
>   netlink: add notification monitor
>   move shared code into a common file
>   netlink: add bitset helpers
>   netlink: partial netlink handler for gset (no option)
>   netlink: support getting wake-on-lan and debugging settings
>   netlink: add basic command line parsing helpers
>   netlink: add bitset command line parser handlers
>   netlink: add netlink handler for sset (-s)
>   netlink: support tests with netlink enabled
>   netlink: add handler for permaddr (-P)
>   netlink: support for pretty printing netlink messages
>   netlink: message format description for ethtool netlink
>   netlink: message format descriptions for genetlink control
>   netlink: message format descriptions for rtnetlink
>   netlink: use pretty printing for ethtool netlink messages
> 
>  Makefile.am                                  |   31 +-
>  common.c                                     |  145 +++
>  common.h                                     |   26 +
>  configure.ac                                 |   14 +-
>  ethtool.8.in                                 |   48 +-
>  ethtool.c                                    |  819 ++++++++------
>  internal.h                                   |   31 +-
>  netlink/bitset.c                             |  218 ++++
>  netlink/bitset.h                             |   26 +
>  netlink/desc-ethtool.c                       |  139 +++
>  netlink/desc-genlctrl.c                      |   56 +
>  netlink/desc-rtnl.c                          |   96 ++
>  netlink/extapi.h                             |   46 +
>  netlink/monitor.c                            |  229 ++++
>  netlink/msgbuff.c                            |  255 +++++
>  netlink/msgbuff.h                            |  117 ++
>  netlink/netlink.c                            |  216 ++++
>  netlink/netlink.h                            |   87 ++
>  netlink/nlsock.c                             |  405 +++++++
>  netlink/nlsock.h                             |   45 +
>  netlink/parser.c                             | 1058 ++++++++++++++++++
>  netlink/parser.h                             |  144 +++
>  netlink/permaddr.c                           |  114 ++
>  netlink/prettymsg.c                          |  237 ++++
>  netlink/prettymsg.h                          |  118 ++
>  netlink/settings.c                           |  955 ++++++++++++++++
>  netlink/strset.c                             |  297 +++++
>  netlink/strset.h                             |   25 +
>  test-cmdline.c                               |   29 +-
>  test-features.c                              |   11 +
>  ethtool-copy.h => uapi/linux/ethtool.h       |   17 +
>  uapi/linux/ethtool_netlink.h                 |  237 ++++
>  uapi/linux/genetlink.h                       |   89 ++
>  uapi/linux/if_link.h                         | 1051 +++++++++++++++++
>  net_tstamp-copy.h => uapi/linux/net_tstamp.h |   27 +
>  uapi/linux/netlink.h                         |  248 ++++
>  uapi/linux/rtnetlink.h                       |  777 +++++++++++++
>  37 files changed, 8102 insertions(+), 381 deletions(-)
>  create mode 100644 common.c
>  create mode 100644 common.h
>  create mode 100644 netlink/bitset.c
>  create mode 100644 netlink/bitset.h
>  create mode 100644 netlink/desc-ethtool.c
>  create mode 100644 netlink/desc-genlctrl.c
>  create mode 100644 netlink/desc-rtnl.c
>  create mode 100644 netlink/extapi.h
>  create mode 100644 netlink/monitor.c
>  create mode 100644 netlink/msgbuff.c
>  create mode 100644 netlink/msgbuff.h
>  create mode 100644 netlink/netlink.c
>  create mode 100644 netlink/netlink.h
>  create mode 100644 netlink/nlsock.c
>  create mode 100644 netlink/nlsock.h
>  create mode 100644 netlink/parser.c
>  create mode 100644 netlink/parser.h
>  create mode 100644 netlink/permaddr.c
>  create mode 100644 netlink/prettymsg.c
>  create mode 100644 netlink/prettymsg.h
>  create mode 100644 netlink/settings.c
>  create mode 100644 netlink/strset.c
>  create mode 100644 netlink/strset.h
>  rename ethtool-copy.h => uapi/linux/ethtool.h (99%)
>  create mode 100644 uapi/linux/ethtool_netlink.h
>  create mode 100644 uapi/linux/genetlink.h
>  create mode 100644 uapi/linux/if_link.h
>  rename net_tstamp-copy.h => uapi/linux/net_tstamp.h (84%)
>  create mode 100644 uapi/linux/netlink.h
>  create mode 100644 uapi/linux/rtnetlink.h
> 
> -- 
> 2.25.1

Just a quick check -- executing "./autogen.sh ; ./configure ; make
distcheck" fails with the attached log output.

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

[-- Attachment #2: make-distcheck.log --]
[-- Type: text/plain, Size: 7588 bytes --]

make  dist-gzip am__post_remove_distdir='@:'
make[1]: Entering directory '/home/linville/git/ethtool'
make  distdir-am
make[2]: Entering directory '/home/linville/git/ethtool'
if test -d "ethtool-5.4"; then find "ethtool-5.4" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "ethtool-5.4" || { sleep 5 && rm -rf "ethtool-5.4"; }; else :; fi
test -d "ethtool-5.4" || mkdir "ethtool-5.4"
make  \
  top_distdir="ethtool-5.4" distdir="ethtool-5.4" \
  dist-hook
make[3]: Entering directory '/home/linville/git/ethtool'
cp ./ethtool.spec ethtool-5.4
make[3]: Leaving directory '/home/linville/git/ethtool'
test -n "" \
|| find "ethtool-5.4" -type d ! -perm -755 \
	-exec chmod u+rwx,go+rx {} \; -o \
  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -444 -exec /bin/sh /home/linville/git/ethtool/install-sh -c -m a+r {} {} \; \
|| chmod -R a+r "ethtool-5.4"
make[2]: Leaving directory '/home/linville/git/ethtool'
tardir=ethtool-5.4 && ${TAR-tar} chof - "$tardir" | eval GZIP= gzip --best -c >ethtool-5.4.tar.gz
make[1]: Leaving directory '/home/linville/git/ethtool'
if test -d "ethtool-5.4"; then find "ethtool-5.4" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "ethtool-5.4" || { sleep 5 && rm -rf "ethtool-5.4"; }; else :; fi
case 'ethtool-5.4.tar.gz' in \
*.tar.gz*) \
  eval GZIP= gzip --best -dc ethtool-5.4.tar.gz | ${TAR-tar} xf - ;;\
*.tar.bz2*) \
  bzip2 -dc ethtool-5.4.tar.bz2 | ${TAR-tar} xf - ;;\
*.tar.lz*) \
  lzip -dc ethtool-5.4.tar.lz | ${TAR-tar} xf - ;;\
*.tar.xz*) \
  xz -dc ethtool-5.4.tar.xz | ${TAR-tar} xf - ;;\
*.tar.Z*) \
  uncompress -c ethtool-5.4.tar.Z | ${TAR-tar} xf - ;;\
*.shar.gz*) \
  eval GZIP= gzip --best -dc ethtool-5.4.shar.gz | unshar ;;\
*.zip*) \
  unzip ethtool-5.4.zip ;;\
esac
chmod -R a-w ethtool-5.4
chmod u+w ethtool-5.4
mkdir ethtool-5.4/_build ethtool-5.4/_build/sub ethtool-5.4/_inst
chmod a-w ethtool-5.4
test -d ethtool-5.4/_build || exit 0; \
dc_install_base=`CDPATH="${ZSH_VERSION+.}:" && cd ethtool-5.4/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
  && dc_destdir="${TMPDIR-/tmp}/am-dc-$$/" \
  && am__cwd=`pwd` \
  && CDPATH="${ZSH_VERSION+.}:" && cd ethtool-5.4/_build/sub \
  && ../../configure \
     \
     \
    --srcdir=../.. --prefix="$dc_install_base" \
  && make  \
  && make  dvi \
  && make  check \
  && make  install \
  && make  installcheck \
  && make  uninstall \
  && make  distuninstallcheck_dir="$dc_install_base" \
        distuninstallcheck \
  && chmod -R a-w "$dc_install_base" \
  && ({ \
       (cd ../.. && umask 077 && mkdir "$dc_destdir") \
       && make  DESTDIR="$dc_destdir" install \
       && make  DESTDIR="$dc_destdir" uninstall \
       && make  DESTDIR="$dc_destdir" \
            distuninstallcheck_dir="$dc_destdir" distuninstallcheck; \
      } || { rm -rf "$dc_destdir"; exit 1; }) \
  && rm -rf "$dc_destdir" \
  && make  dist \
  && rm -rf ethtool-5.4.tar.gz \
  && make  distcleancheck \
  && cd "$am__cwd" \
  || exit 1
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking whether to enable maintainer-specific portions of Makefiles... no
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking whether make supports the include directive... yes (GNU style)
checking dependency style of gcc... gcc3
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking whether gcc needs -traditional... no
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking whether <linux/types.h> defines big-endian types... yes
checking for ANSI C header files... yes
checking for socket... yes
checking for strtol... yes
checking for bash-completion directory... ${prefix}/share/bash-completion/completions
checking for pkg-config... (cached) /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for MNL... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating ethtool.spec
config.status: creating ethtool.8
config.status: creating ethtool-config.h
config.status: executing depfiles commands
make[1]: Entering directory '/home/linville/git/ethtool/ethtool-5.4/_build/sub'
make  all-am
make[2]: Entering directory '/home/linville/git/ethtool/ethtool-5.4/_build/sub'
gcc -DHAVE_CONFIG_H -I. -I../..    -I./uapi -Wall  -g -O2 -MT ethtool-ethtool.o -MD -MP -MF .deps/ethtool-ethtool.Tpo -c -o ethtool-ethtool.o `test -f 'ethtool.c' || echo '../../'`ethtool.c
../../ethtool.c: In function ‘do_get_phy_tunable’:
../../ethtool.c:4773:16: error: ‘ETHTOOL_PHY_EDPD’ undeclared (first use in this function); did you mean ‘ETHTOOL_PHYS_ID’?
 4773 |   cont.ds.id = ETHTOOL_PHY_EDPD;
      |                ^~~~~~~~~~~~~~~~
      |                ETHTOOL_PHYS_ID
../../ethtool.c:4773:16: note: each undeclared identifier is reported only once for each function it appears in
../../ethtool.c:4781:21: error: ‘ETHTOOL_PHY_EDPD_DISABLE’ undeclared (first use in this function); did you mean ‘ETHTOOL_PHY_STUNABLE’?
 4781 |   if (cont.msecs == ETHTOOL_PHY_EDPD_DISABLE)
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~
      |                     ETHTOOL_PHY_STUNABLE
../../ethtool.c:4783:26: error: ‘ETHTOOL_PHY_EDPD_NO_TX’ undeclared (first use in this function)
 4783 |   else if (cont.msecs == ETHTOOL_PHY_EDPD_NO_TX)
      |                          ^~~~~~~~~~~~~~~~~~~~~~
../../ethtool.c: In function ‘do_set_phy_tunable’:
../../ethtool.c:4962:25: error: ‘ETHTOOL_PHY_EDPD_DFLT_TX_MSECS’ undeclared (first use in this function)
 4962 |  u16 edpd_tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../../ethtool.c:5003:23: error: ‘ETHTOOL_PHY_EDPD_DISABLE’ undeclared (first use in this function); did you mean ‘ETHTOOL_PHY_STUNABLE’?
 5003 |    edpd_tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~
      |                       ETHTOOL_PHY_STUNABLE
../../ethtool.c:5005:23: error: ‘ETHTOOL_PHY_EDPD_NO_TX’ undeclared (first use in this function)
 5005 |    edpd_tx_interval = ETHTOOL_PHY_EDPD_NO_TX;
      |                       ^~~~~~~~~~~~~~~~~~~~~~
../../ethtool.c:5053:17: error: ‘ETHTOOL_PHY_EDPD’ undeclared (first use in this function); did you mean ‘ETHTOOL_PHYS_ID’?
 5053 |   cont.fld.id = ETHTOOL_PHY_EDPD;
      |                 ^~~~~~~~~~~~~~~~
      |                 ETHTOOL_PHYS_ID
make[2]: *** [Makefile:1255: ethtool-ethtool.o] Error 1
make[2]: Leaving directory '/home/linville/git/ethtool/ethtool-5.4/_build/sub'
make[1]: *** [Makefile:881: all] Error 2
make[1]: Leaving directory '/home/linville/git/ethtool/ethtool-5.4/_build/sub'
make: *** [Makefile:3670: distcheck] Error 1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release
  2020-03-05 19:24 ` John W. Linville
@ 2020-03-05 20:32   ` Michal Kubecek
  2020-03-05 21:14     ` Michal Kubecek
  0 siblings, 1 reply; 30+ messages in thread
From: Michal Kubecek @ 2020-03-05 20:32 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Andrew Lunn, Florian Fainelli

On Thu, Mar 05, 2020 at 02:24:16PM -0500, John W. Linville wrote:
> 
> Just a quick check -- executing "./autogen.sh ; ./configure ; make
> distcheck" fails with the attached log output.
[...]
> make[2]: Entering directory '/home/linville/git/ethtool/ethtool-5.4/_build/sub'
> gcc -DHAVE_CONFIG_H -I. -I../..    -I./uapi -Wall  -g -O2 -MT ethtool-ethtool.o -MD -MP -MF .deps/ethtool-ethtool.Tpo -c -o ethtool-ethtool.o `test -f 'ethtool.c' || echo '../../'`ethtool.c

I can see what is going on: this runs in subdirectory and correctly adds
"-I../.." but not "-I../../uapi". I'm afraid I'll have to dive into
automake documentation to see how to make it adjust that path as well.

> ../../ethtool.c: In function ‘do_get_phy_tunable’:
> ../../ethtool.c:4773:16: error: ‘ETHTOOL_PHY_EDPD’ undeclared (first use in this function); did you mean ‘ETHTOOL_PHYS_ID’?
>  4773 |   cont.ds.id = ETHTOOL_PHY_EDPD;
>       |                ^~~~~~~~~~~~~~~~
>       |                ETHTOOL_PHYS_ID

This is a result of the missing include path above: instead of
up-to-date uapi/linux/ethtool.h, older system file from /usr/include is
used so that new additions are missing. I have many more errors like
this and when I tried to rename /usr/include/linux/ethtool.h, the build
failed with

  ../../internal.h:56:10: fatal error: linux/ethtool.h: No such file or directory

I'll try to find what is the right way to add an include directory,
adding "-I./uapi" to AM_CFLAGS did the trick for regular build but
clearly isn't sufficient for other targets.

Michal

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release
  2020-03-05 20:32   ` Michal Kubecek
@ 2020-03-05 21:14     ` Michal Kubecek
  0 siblings, 0 replies; 30+ messages in thread
From: Michal Kubecek @ 2020-03-05 21:14 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Andrew Lunn, Florian Fainelli

On Thu, Mar 05, 2020 at 09:32:59PM +0100, Michal Kubecek wrote:
> On Thu, Mar 05, 2020 at 02:24:16PM -0500, John W. Linville wrote:
> > 
> > Just a quick check -- executing "./autogen.sh ; ./configure ; make
> > distcheck" fails with the attached log output.
> [...]
> > make[2]: Entering directory '/home/linville/git/ethtool/ethtool-5.4/_build/sub'
> > gcc -DHAVE_CONFIG_H -I. -I../..    -I./uapi -Wall  -g -O2 -MT ethtool-ethtool.o -MD -MP -MF .deps/ethtool-ethtool.Tpo -c -o ethtool-ethtool.o `test -f 'ethtool.c' || echo '../../'`ethtool.c
> 
> I can see what is going on: this runs in subdirectory and correctly adds
> "-I../.." but not "-I../../uapi". I'm afraid I'll have to dive into
> automake documentation to see how to make it adjust that path as well.
> 
> > ../../ethtool.c: In function ‘do_get_phy_tunable’:
> > ../../ethtool.c:4773:16: error: ‘ETHTOOL_PHY_EDPD’ undeclared (first use in this function); did you mean ‘ETHTOOL_PHYS_ID’?
> >  4773 |   cont.ds.id = ETHTOOL_PHY_EDPD;
> >       |                ^~~~~~~~~~~~~~~~
> >       |                ETHTOOL_PHYS_ID
> 
> This is a result of the missing include path above: instead of
> up-to-date uapi/linux/ethtool.h, older system file from /usr/include is
> used so that new additions are missing. I have many more errors like
> this and when I tried to rename /usr/include/linux/ethtool.h, the build
> failed with
> 
>   ../../internal.h:56:10: fatal error: linux/ethtool.h: No such file or directory
> 
> I'll try to find what is the right way to add an include directory,
> adding "-I./uapi" to AM_CFLAGS did the trick for regular build but
> clearly isn't sufficient for other targets.

The change below seems to fix the problem but I'll run some more tests
tomorrow to see that it does not break anything.

Michal

diff --git a/Makefile.am b/Makefile.am
index 2fd79eb8c79a..eae5a55ce933 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,5 @@
-AM_CFLAGS = -I./uapi -Wall
+AM_CFLAGS = -Wall
+AM_CPPFLAGS = -I$(top_srcdir)/uapi
 LDADD = -lm
 
 man_MANS = ethtool.8

^ permalink raw reply related	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2020-03-05 21:14 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-04 20:24 [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
2020-03-04 20:24 ` [PATCH ethtool v2 01/25] move UAPI header copies to a separate directory Michal Kubecek
2020-03-04 20:24 ` [PATCH ethtool v2 02/25] update UAPI header copies Michal Kubecek
2020-03-04 20:24 ` [PATCH ethtool v2 03/25] add --debug option to control debugging messages Michal Kubecek
2020-03-04 20:24 ` [PATCH ethtool v2 04/25] use named initializers in command line option list Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 05/25] netlink: add netlink related UAPI header files Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 06/25] netlink: introduce the netlink interface Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 07/25] netlink: message buffer and composition helpers Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 08/25] netlink: netlink socket wrapper and helpers Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 09/25] netlink: initialize ethtool netlink socket Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 10/25] netlink: add support for string sets Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 11/25] netlink: add notification monitor Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 12/25] move shared code into a common file Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 13/25] netlink: add bitset helpers Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 14/25] netlink: partial netlink handler for gset (no option) Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 15/25] netlink: support getting wake-on-lan and debugging settings Michal Kubecek
2020-03-04 20:25 ` [PATCH ethtool v2 16/25] netlink: add basic command line parsing helpers Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 17/25] netlink: add bitset command line parser handlers Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 18/25] netlink: add netlink handler for sset (-s) Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 19/25] netlink: support tests with netlink enabled Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 20/25] netlink: add handler for permaddr (-P) Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 21/25] netlink: support for pretty printing netlink messages Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 22/25] netlink: message format description for ethtool netlink Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 23/25] netlink: message format descriptions for genetlink control Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 24/25] netlink: message format descriptions for rtnetlink Michal Kubecek
2020-03-04 20:26 ` [PATCH ethtool v2 25/25] netlink: use pretty printing for ethtool netlink messages Michal Kubecek
2020-03-04 20:34 ` [PATCH ethtool v2 00/25] initial netlink interface implementation for 5.6 release Michal Kubecek
2020-03-05 19:24 ` John W. Linville
2020-03-05 20:32   ` Michal Kubecek
2020-03-05 21:14     ` Michal Kubecek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).