From patchwork Wed Mar 4 20:24:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249206 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XljS44Rvz9sPF for ; Thu, 5 Mar 2020 07:24:44 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729217AbgCDUYn (ORCPT ); Wed, 4 Mar 2020 15:24:43 -0500 Received: from mx2.suse.de ([195.135.220.15]:33110 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUYn (ORCPT ); Wed, 4 Mar 2020 15:24:43 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 5586AAF01; Wed, 4 Mar 2020 20:24:41 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 02E90E037F; Wed, 4 Mar 2020 21:24:40 +0100 (CET) Message-Id: <23ae41de7dd8663380d522e0fa2a75bf6b8724af.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 01/25] move UAPI header copies to a separate directory To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:24:40 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 +#include #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 From patchwork Wed Mar 4 20:24:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249207 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XljZ2X6kz9sP7 for ; Thu, 5 Mar 2020 07:24:50 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729460AbgCDUYt (ORCPT ); Wed, 4 Mar 2020 15:24:49 -0500 Received: from mx2.suse.de ([195.135.220.15]:33140 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUYt (ORCPT ); Wed, 4 Mar 2020 15:24:49 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 5C5CCAD41; Wed, 4 Mar 2020 20:24:46 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 0A14DE037F; Wed, 4 Mar 2020 21:24:46 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 02/25] update UAPI header copies To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:24:46 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Update to v5.6-rc1. Signed-off-by: Michal Kubecek --- 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 */ From patchwork Wed Mar 4 20:24:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249208 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xljf2YNkz9sP7 for ; Thu, 5 Mar 2020 07:24:54 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729643AbgCDUYx (ORCPT ); Wed, 4 Mar 2020 15:24:53 -0500 Received: from mx2.suse.de ([195.135.220.15]:33162 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUYx (ORCPT ); Wed, 4 Mar 2020 15:24:53 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 5C61CADFE; Wed, 4 Mar 2020 20:24:51 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 10742E037F; Wed, 4 Mar 2020 21:24:51 +0100 (CET) Message-Id: <1e01f6e1890d816966a509a6257e84a48fe40533.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 03/25] add --debug option to control debugging messages To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:24:51 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 From patchwork Wed Mar 4 20:24:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249209 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xljm5q1Nz9sP7 for ; Thu, 5 Mar 2020 07:25:00 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387955AbgCDUZA (ORCPT ); Wed, 4 Mar 2020 15:25:00 -0500 Received: from mx2.suse.de ([195.135.220.15]:33188 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUY7 (ORCPT ); Wed, 4 Mar 2020 15:24:59 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 6C0BAAF82; Wed, 4 Mar 2020 20:24:56 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 16DE9E037F; Wed, 4 Mar 2020 21:24:56 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 04/25] use named initializers in command line option list To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:24:56 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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--; From patchwork Wed Mar 4 20:25:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249211 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xlk11Dmkz9sP7 for ; Thu, 5 Mar 2020 07:25:13 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388031AbgCDUZM (ORCPT ); Wed, 4 Mar 2020 15:25:12 -0500 Received: from mx2.suse.de ([195.135.220.15]:33210 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387966AbgCDUZL (ORCPT ); Wed, 4 Mar 2020 15:25:11 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 8C5BAB153; Wed, 4 Mar 2020 20:25:01 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 1D816E037F; Wed, 4 Mar 2020 21:25:01 +0100 (CET) Message-Id: <8ed2e8992af7ed1e9cd100fcd5e94cdabea03694.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 05/25] netlink: add netlink related UAPI header files To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:01 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add copies of kernel UAPI header files needed for netlink interface: The (sanitized) copies are taken from v5.6-rc1. v2: - add linux/rtnetlink.h and linux/if_link.h Signed-off-by: Michal Kubecek --- 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 + +/* 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 +#include + +#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 +#include + +/* 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 +#include /* for __kernel_sa_family_t */ +#include + +#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 +#include +#include +#include +#include + +/* 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 */ From patchwork Wed Mar 4 20:25:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249210 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xljy4xpfz9sP7 for ; Thu, 5 Mar 2020 07:25:10 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388011AbgCDUZJ (ORCPT ); Wed, 4 Mar 2020 15:25:09 -0500 Received: from mx2.suse.de ([195.135.220.15]:33232 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387969AbgCDUZJ (ORCPT ); Wed, 4 Mar 2020 15:25:09 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 7ADDDB199; Wed, 4 Mar 2020 20:25:06 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 242FFE037F; Wed, 4 Mar 2020 21:25:06 +0100 (CET) Message-Id: <531b909d779b6ec1942f56f7a17ef686c83d3917.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 06/25] netlink: introduce the netlink interface To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:06 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 #include +#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 */ #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 #include @@ -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 + +#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 +#include +#include +#include + +#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__ */ From patchwork Wed Mar 4 20:25:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249212 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xlk36XlSz9sPF for ; Thu, 5 Mar 2020 07:25:15 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388146AbgCDUZP (ORCPT ); Wed, 4 Mar 2020 15:25:15 -0500 Received: from mx2.suse.de ([195.135.220.15]:33282 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387966AbgCDUZO (ORCPT ); Wed, 4 Mar 2020 15:25:14 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 7B6AEAE9A; Wed, 4 Mar 2020 20:25:11 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 2ACCAE037F; Wed, 4 Mar 2020 21:25:11 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 07/25] netlink: message buffer and composition helpers To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:11 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 +#include +#include +#include + +#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 +#include +#include +#include + +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__ */ From patchwork Wed Mar 4 20:25:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249213 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xlk90rt6z9sP7 for ; Thu, 5 Mar 2020 07:25:21 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388226AbgCDUZU (ORCPT ); Wed, 4 Mar 2020 15:25:20 -0500 Received: from mx2.suse.de ([195.135.220.15]:33302 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387966AbgCDUZT (ORCPT ); Wed, 4 Mar 2020 15:25:19 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 835CCB28D; Wed, 4 Mar 2020 20:25:16 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 317ECE037F; Wed, 4 Mar 2020 21:25:16 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 08/25] netlink: netlink socket wrapper and helpers To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:16 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 +#include + +#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 +#include +#include +#include +#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__ */ From patchwork Wed Mar 4 20:25:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249214 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XlkF3Rh0z9sPF for ; Thu, 5 Mar 2020 07:25:25 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388272AbgCDUZY (ORCPT ); Wed, 4 Mar 2020 15:25:24 -0500 Received: from mx2.suse.de ([195.135.220.15]:33346 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388230AbgCDUZY (ORCPT ); Wed, 4 Mar 2020 15:25:24 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 83BE7B240; Wed, 4 Mar 2020 20:25:21 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 37E84E037F; Wed, 4 Mar 2020 21:25:21 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 09/25] netlink: initialize ethtool netlink socket To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:21 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 #include #include +#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__ */ From patchwork Wed Mar 4 20:25:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249215 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XlkM3HHtz9sPF for ; Thu, 5 Mar 2020 07:25:31 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388290AbgCDUZa (ORCPT ); Wed, 4 Mar 2020 15:25:30 -0500 Received: from mx2.suse.de ([195.135.220.15]:33424 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388230AbgCDUZa (ORCPT ); Wed, 4 Mar 2020 15:25:30 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 8F291B2A2; Wed, 4 Mar 2020 20:25:26 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 3E923E037F; Wed, 4 Mar 2020 21:25:26 +0100 (CET) Message-Id: <8c607619bcf65c21a7dc4da11a84625568342438.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 10/25] netlink: add support for string sets To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:26 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 +#include + +#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__ */ From patchwork Wed Mar 4 20:25:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249216 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XlkR6zqhz9sP7 for ; Thu, 5 Mar 2020 07:25:35 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388307AbgCDUZf (ORCPT ); Wed, 4 Mar 2020 15:25:35 -0500 Received: from mx2.suse.de ([195.135.220.15]:33458 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUZe (ORCPT ); Wed, 4 Mar 2020 15:25:34 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 96CFBB2A5; Wed, 4 Mar 2020 20:25:31 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 45FD0E037F; Wed, 4 Mar 2020 21:25:31 +0100 (CET) Message-Id: <5ca8a91fd1cc6be16b8959d4fd5d5ce83701fe4c.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 11/25] netlink: add notification monitor To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:31 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 + +#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; From patchwork Wed Mar 4 20:25:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249217 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XlkX6Bkpz9sP7 for ; Thu, 5 Mar 2020 07:25:40 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729764AbgCDUZk (ORCPT ); Wed, 4 Mar 2020 15:25:40 -0500 Received: from mx2.suse.de ([195.135.220.15]:33566 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUZj (ORCPT ); Wed, 4 Mar 2020 15:25:39 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 9E4F2AE99; Wed, 4 Mar 2020 20:25:36 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 4CAFEE037F; Wed, 4 Mar 2020 21:25:36 +0100 (CET) Message-Id: <64325650e41b075ec5cb285b58cb447e7e00df4d.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 12/25] move shared code into a common file To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:36 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 #include +#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, From patchwork Wed Mar 4 20:25:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249218 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xlkf5fFJz9sP7 for ; Thu, 5 Mar 2020 07:25:46 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729857AbgCDUZq (ORCPT ); Wed, 4 Mar 2020 15:25:46 -0500 Received: from mx2.suse.de ([195.135.220.15]:33618 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUZp (ORCPT ); Wed, 4 Mar 2020 15:25:45 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id A39F7AD41; Wed, 4 Mar 2020 20:25:41 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 53101E037F; Wed, 4 Mar 2020 21:25:41 +0100 (CET) Message-Id: <48a31badbefa340dad67752177a4057c6acb745c.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 13/25] netlink: add bitset helpers To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:41 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 +#include + +#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 +#include +#include +#include +#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__ */ From patchwork Wed Mar 4 20:25:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249219 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xlkl5Cjpz9sPF for ; Thu, 5 Mar 2020 07:25:51 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388317AbgCDUZu (ORCPT ); Wed, 4 Mar 2020 15:25:50 -0500 Received: from mx2.suse.de ([195.135.220.15]:33660 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728482AbgCDUZu (ORCPT ); Wed, 4 Mar 2020 15:25:50 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id AE2A2ADFE; Wed, 4 Mar 2020 20:25:46 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 597EBE037F; Wed, 4 Mar 2020 21:25:46 +0100 (CET) Message-Id: <82b0d11b47550431e01cd7009a668674545df754.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 14/25] netlink: partial netlink handler for gset (no option) To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:46 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Partially implement "ethtool " 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 --- 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 " and "ethtool -s ...". + */ + +#include +#include +#include + +#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; +} From patchwork Wed Mar 4 20:25:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249220 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xlkq1Q4xz9sP7 for ; Thu, 5 Mar 2020 07:25:55 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388334AbgCDUZy (ORCPT ); Wed, 4 Mar 2020 15:25:54 -0500 Received: from mx2.suse.de ([195.135.220.15]:33706 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388327AbgCDUZx (ORCPT ); Wed, 4 Mar 2020 15:25:53 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id AEA0DAF01; Wed, 4 Mar 2020 20:25:51 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 5FE04E037F; Wed, 4 Mar 2020 21:25:51 +0100 (CET) Message-Id: <822930f2261c04da4a02dcd7d8b1e4aed360d072.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 15/25] netlink: support getting wake-on-lan and debugging settings To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:51 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Finish "ethtool " 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 --- 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; } From patchwork Wed Mar 4 20:25:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249221 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xlkx1XmZz9sP7 for ; Thu, 5 Mar 2020 07:26:01 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388341AbgCDU0A (ORCPT ); Wed, 4 Mar 2020 15:26:00 -0500 Received: from mx2.suse.de ([195.135.220.15]:33752 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388327AbgCDU0A (ORCPT ); Wed, 4 Mar 2020 15:26:00 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id BB14DAF82; Wed, 4 Mar 2020 20:25:56 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 668D7E037F; Wed, 4 Mar 2020 21:25:56 +0100 (CET) Message-Id: <2594bd8a5276bfa7ee77505cc2f68d2cbdeadfba.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 16/25] netlink: add basic command line parsing helpers To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:25:56 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 +#include +#include +#include + +#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 + +#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__ */ From patchwork Wed Mar 4 20:26:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249222 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xll21WdPz9sP7 for ; Thu, 5 Mar 2020 07:26:06 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388344AbgCDU0F (ORCPT ); Wed, 4 Mar 2020 15:26:05 -0500 Received: from mx2.suse.de ([195.135.220.15]:33782 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388327AbgCDU0F (ORCPT ); Wed, 4 Mar 2020 15:26:05 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id BFBA9B2A4; Wed, 4 Mar 2020 20:26:01 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 6D2E1E037F; Wed, 4 Mar 2020 21:26:01 +0100 (CET) Message-Id: <85121f2a8fc0a04c2109936d48c926c465054b00.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 17/25] netlink: add bitset command line parser handlers To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:01 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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, From patchwork Wed Mar 4 20:26:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249223 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xll76Gc3z9sPF for ; Thu, 5 Mar 2020 07:26:11 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388353AbgCDU0K (ORCPT ); Wed, 4 Mar 2020 15:26:10 -0500 Received: from mx2.suse.de ([195.135.220.15]:33816 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388327AbgCDU0K (ORCPT ); Wed, 4 Mar 2020 15:26:10 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id C9C38B2B6; Wed, 4 Mar 2020 20:26:06 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 73AD0E037F; Wed, 4 Mar 2020 21:26:06 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 18/25] netlink: add netlink handler for sset (-s) To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:06 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement "ethtool -s " 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 --- 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; +} From patchwork Wed Mar 4 20:26:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249224 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XllB6510z9sP7 for ; Thu, 5 Mar 2020 07:26:14 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388359AbgCDU0O (ORCPT ); Wed, 4 Mar 2020 15:26:14 -0500 Received: from mx2.suse.de ([195.135.220.15]:33846 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388327AbgCDU0N (ORCPT ); Wed, 4 Mar 2020 15:26:13 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id CFA8AB2A6; Wed, 4 Mar 2020 20:26:11 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 7A279E037F; Wed, 4 Mar 2020 21:26:11 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 19/25] netlink: support tests with netlink enabled To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:11 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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; From patchwork Wed Mar 4 20:26:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249225 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XllJ4Dxfz9sP7 for ; Thu, 5 Mar 2020 07:26:20 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388365AbgCDU0T (ORCPT ); Wed, 4 Mar 2020 15:26:19 -0500 Received: from mx2.suse.de ([195.135.220.15]:33874 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388327AbgCDU0T (ORCPT ); Wed, 4 Mar 2020 15:26:19 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id D16EFB2AB; Wed, 4 Mar 2020 20:26:16 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 80CCEE037F; Wed, 4 Mar 2020 21:26:16 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 20/25] netlink: add handler for permaddr (-P) To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:16 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement "ethtool -P " 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 --- 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 " + */ + +#include +#include +#include +#include +#include + +#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); +} From patchwork Wed Mar 4 20:26:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249226 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XllP6nlnz9sP7 for ; Thu, 5 Mar 2020 07:26:25 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388373AbgCDU0Y (ORCPT ); Wed, 4 Mar 2020 15:26:24 -0500 Received: from mx2.suse.de ([195.135.220.15]:33890 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387398AbgCDU0Y (ORCPT ); Wed, 4 Mar 2020 15:26:24 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id D780BB2B4; Wed, 4 Mar 2020 20:26:21 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 87430E037F; Wed, 4 Mar 2020 21:26:21 +0100 (CET) Message-Id: <54bf747aa2e8d890096c865d06414d3f7ec164a6.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 21/25] netlink: support for pretty printing netlink messages To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:21 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +/* 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__ */ From patchwork Wed Mar 4 20:26:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249227 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48XllV5x1nz9sP7 for ; Thu, 5 Mar 2020 07:26:30 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388377AbgCDU0a (ORCPT ); Wed, 4 Mar 2020 15:26:30 -0500 Received: from mx2.suse.de ([195.135.220.15]:33918 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387926AbgCDU03 (ORCPT ); Wed, 4 Mar 2020 15:26:29 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id DDC38B2B8; Wed, 4 Mar 2020 20:26:26 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 8DBAAE037F; Wed, 4 Mar 2020 21:26:26 +0100 (CET) Message-Id: <4b49cd7e618bf0eee6d8dede639ee92167049496.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 22/25] netlink: message format description for ethtool netlink To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:26 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 + +#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__ */ From patchwork Wed Mar 4 20:26:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249228 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xllb4TWHz9sPF for ; Thu, 5 Mar 2020 07:26:35 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388387AbgCDU0e (ORCPT ); Wed, 4 Mar 2020 15:26:34 -0500 Received: from mx2.suse.de ([195.135.220.15]:33954 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387926AbgCDU0e (ORCPT ); Wed, 4 Mar 2020 15:26:34 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id E78EDB2BC; Wed, 4 Mar 2020 20:26:31 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 94421E037F; Wed, 4 Mar 2020 21:26:31 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 23/25] netlink: message format descriptions for genetlink control To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:31 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 + +#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__ */ From patchwork Wed Mar 4 20:26:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249229 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xllh4t80z9sP7 for ; Thu, 5 Mar 2020 07:26:40 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388418AbgCDU0k (ORCPT ); Wed, 4 Mar 2020 15:26:40 -0500 Received: from mx2.suse.de ([195.135.220.15]:34006 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387926AbgCDU0j (ORCPT ); Wed, 4 Mar 2020 15:26:39 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id EAB68B199; Wed, 4 Mar 2020 20:26:36 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 9AE1CE037F; Wed, 4 Mar 2020 21:26:36 +0100 (CET) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 24/25] netlink: message format descriptions for rtnetlink To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:36 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 + +#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__ */ From patchwork Wed Mar 4 20:26:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1249230 X-Patchwork-Delegate: linville@tuxdriver.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48Xllp2pkbz9sP7 for ; Thu, 5 Mar 2020 07:26:46 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388423AbgCDU0p (ORCPT ); Wed, 4 Mar 2020 15:26:45 -0500 Received: from mx2.suse.de ([195.135.220.15]:34054 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387926AbgCDU0o (ORCPT ); Wed, 4 Mar 2020 15:26:44 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id F12EAB240; Wed, 4 Mar 2020 20:26:41 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id A1B5DE037F; Wed, 4 Mar 2020 21:26:41 +0100 (CET) Message-Id: <5d787f17d58fb66c671a90d2cfc8f47eaaf730e8.1583347351.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool v2 25/25] netlink: use pretty printing for ethtool netlink messages To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn , Florian Fainelli Date: Wed, 4 Mar 2020 21:26:41 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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);