Netdev Archive on lore.kernel.org
 help / color / Atom feed
From: Michal Kubecek <mkubecek@suse.cz>
To: John Linville <linville@tuxdriver.com>, netdev@vger.kernel.org
Cc: Andrew Lunn <andrew@lunn.ch>, Florian Fainelli <f.fainelli@gmail.com>
Subject: [PATCH ethtool 06/19] netlink: introduce the netlink interface
Date: Sun, 16 Feb 2020 23:47:00 +0100 (CET)
Message-ID: <4574f6130651b56da57b277dddecc4e188c807df.1581892124.git.mkubecek@suse.cz> (raw)
In-Reply-To: <cover.1581892124.git.mkubecek@suse.cz>

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.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       | 17 +++++++++--
 configure.ac      | 14 ++++++++-
 ethtool.c         | 76 +++++++++++++++++++++++++++++++++++------------
 internal.h        | 12 ++++++++
 netlink/extapi.h  | 31 +++++++++++++++++++
 netlink/netlink.c | 36 ++++++++++++++++++++++
 netlink/netlink.h | 26 ++++++++++++++++
 7 files changed, 190 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..3748d0df5608 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,23 @@ 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
+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..610beccbefda
--- /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;
+	bool			suppress_nlerr;
+};
+
+#endif /* ETHTOOL_NETLINK_INT_H__ */
-- 
2.25.0


  parent reply index

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-16 22:46 [PATCH ethtool 00/19] initial netlink interface implementation for 5.6 release Michal Kubecek
2020-02-16 22:46 ` [PATCH ethtool 01/19] move UAPI header copies to a separate directory Michal Kubecek
2020-02-16 22:46 ` [PATCH ethtool 02/19] update UAPI header copies Michal Kubecek
2020-02-16 22:46 ` [PATCH ethtool 03/19] add --debug option to control debugging messages Michal Kubecek
2020-02-16 22:46 ` [PATCH ethtool 04/19] use named initializers in command line option list Michal Kubecek
2020-02-16 22:46 ` [PATCH ethtool 05/19] netlink: add netlink related UAPI header files Michal Kubecek
2020-02-16 22:47 ` Michal Kubecek [this message]
2020-02-16 22:47 ` [PATCH ethtool 07/19] netlink: message buffer and composition helpers Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 08/19] netlink: netlink socket wrapper and helpers Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 09/19] netlink: initialize ethtool netlink socket Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 10/19] netlink: add support for string sets Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 11/19] netlink: add notification monitor Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 12/19] move shared code into a common file Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 13/19] netlink: add bitset helpers Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 14/19] netlink: partial netlink handler for gset (no option) Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 15/19] netlink: support getting wake-on-lan and debugging settings Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 16/19] netlink: add basic command line parsing helpers Michal Kubecek
2020-02-16 22:47 ` [PATCH ethtool 17/19] netlink: add bitset command line parser handlers Michal Kubecek
2020-02-16 22:48 ` [PATCH ethtool 18/19] netlink: add netlink handler for sset (-s) Michal Kubecek
2020-02-16 22:48 ` [PATCH ethtool 19/19] netlink: support tests with netlink enabled Michal Kubecek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4574f6130651b56da57b277dddecc4e188c807df.1581892124.git.mkubecek@suse.cz \
    --to=mkubecek@suse.cz \
    --cc=andrew@lunn.ch \
    --cc=f.fainelli@gmail.com \
    --cc=linville@tuxdriver.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Netdev Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/netdev/0 netdev/git/0.git
	git clone --mirror https://lore.kernel.org/netdev/1 netdev/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 netdev netdev/ https://lore.kernel.org/netdev \
		netdev@vger.kernel.org
	public-inbox-index netdev

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.netdev


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git