Messages in this thread Patch in this message | | | From | Michal Kubecek <> | Subject | [RFC PATCH ethtool v2 07/23] netlink: add netlink handler for gset (no option) | Date | Mon, 30 Jul 2018 14:56:19 +0200 (CEST) |
| |
Implement "ethtool <dev>" subcommand using netlink interface command ETHNL_CMD_GET_SETTINGS.
Move some output helpers used by both ioctl() and netlink from ethtool.c into new file common.c.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz> --- Makefile.am | 4 +- common.c | 285 ++++++++++++++++++++++++++++++++++++++++++ common.h | 60 +++++++++ ethtool.c | 151 ++--------------------- netlink/extapi.h | 1 + netlink/monitor.c | 14 +++ netlink/netlink.c | 120 ++++++++++++++++++ netlink/netlink.h | 4 + netlink/settings.c | 301 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 796 insertions(+), 144 deletions(-) create mode 100644 common.c create mode 100644 common.h create mode 100644 netlink/settings.c
diff --git a/Makefile.am b/Makefile.am index 7e24fe8c50f4..629f04e1202b 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 = -I./uapi -Wall ethtool_LDADD = -lm if ETHTOOL_ENABLE_PRETTY_DUMP @@ -23,7 +23,7 @@ if ETHTOOL_ENABLE_NETLINK ethtool_SOURCES += \ netlink/netlink.c netlink/netlink.h netlink/extapi.h \ netlink/strset.c netlink/strset.h netlink/monitor.c \ - netlink/drvinfo.c \ + netlink/drvinfo.c netlink/settings.c \ uapi/linux/ethtool_netlink.h \ uapi/linux/netlink.h uapi/linux/genetlink.h ethtool_CFLAGS += @MNL_CFLAGS@ diff --git a/common.c b/common.c new file mode 100644 index 000000000000..59897acd2512 --- /dev/null +++ b/common.c @@ -0,0 +1,285 @@ +#include "internal.h" +#include "common.h" + +#ifndef HAVE_NETIF_MSG +enum { + NETIF_MSG_DRV = 0x0001, + NETIF_MSG_PROBE = 0x0002, + NETIF_MSG_LINK = 0x0004, + NETIF_MSG_TIMER = 0x0008, + NETIF_MSG_IFDOWN = 0x0010, + NETIF_MSG_IFUP = 0x0020, + NETIF_MSG_RX_ERR = 0x0040, + NETIF_MSG_TX_ERR = 0x0080, + NETIF_MSG_TX_QUEUED = 0x0100, + NETIF_MSG_INTR = 0x0200, + NETIF_MSG_TX_DONE = 0x0400, + NETIF_MSG_RX_STATUS = 0x0800, + NETIF_MSG_PKTDATA = 0x1000, + NETIF_MSG_HW = 0x2000, + NETIF_MSG_WOL = 0x4000, +}; +#endif + +const struct flag_info flags_msglvl[] = { + { "drv", NETIF_MSG_DRV }, + { "probe", NETIF_MSG_PROBE }, + { "link", NETIF_MSG_LINK }, + { "timer", NETIF_MSG_TIMER }, + { "ifdown", NETIF_MSG_IFDOWN }, + { "ifup", NETIF_MSG_IFUP }, + { "rx_err", NETIF_MSG_RX_ERR }, + { "tx_err", NETIF_MSG_TX_ERR }, + { "tx_queued", NETIF_MSG_TX_QUEUED }, + { "intr", NETIF_MSG_INTR }, + { "tx_done", NETIF_MSG_TX_DONE }, + { "rx_status", NETIF_MSG_RX_STATUS }, + { "pktdata", NETIF_MSG_PKTDATA }, + { "hw", NETIF_MSG_HW }, + { "wol", NETIF_MSG_WOL }, + {} +}; +const unsigned int n_flags_msglvl = ARRAY_SIZE(flags_msglvl) - 1; + +const char *names_duplex[] = { + [DUPLEX_HALF] = "Half", + [DUPLEX_FULL] = "Full", +}; +DEFINE_ENUM_COUNT(duplex); + +const char *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", +}; +DEFINE_ENUM_COUNT(port); + +const char *names_transceiver[] = { + [XCVR_INTERNAL] = "internal", + [XCVR_EXTERNAL] = "external", +}; +DEFINE_ENUM_COUNT(transceiver); + +/* 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 + */ +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 }, +}; +const unsigned int link_modes_count = ARRAY_SIZE(link_modes); + +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); +} + +void __print_enum(const char *const *info, unsigned int n_info, unsigned int val, + const char *label, const char *unknown) +{ + if (val >= n_info || !info[val]) { + printf("%s", label); + printf(unknown, val); + fputc('\n', stdout); + } else { + printf("%s%s\n", label, info[val]); + } +} + +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'; + } 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..a980b844a0ed --- /dev/null +++ b/common.h @@ -0,0 +1,60 @@ +#ifndef ETHTOOL_COMMON_H__ +#define ETHTOOL_COMMON_H__ + +struct flag_info { + const char *name; + u32 value; +}; + +extern const struct flag_info flags_msglvl[]; +extern const unsigned int n_flags_msglvl; + + +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; +}; + +extern const struct link_mode_info link_modes[]; +extern const unsigned int link_modes_count; + +static inline 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; + + if (mode_class == class) + return true; + return (mode_class == class) || + ((class == LM_CLASS_REAL) && (mode_class = LM_CLASS_UNKNOWN)); +} + +#define DECLARE_ENUM_NAMES(obj) \ + extern const char *names_ ## obj[]; \ + extern const unsigned int names_ ## obj ## _count +#define DEFINE_ENUM_COUNT(obj) \ + const unsigned int names_ ## obj ## _count = ARRAY_SIZE(names_ ## obj) +#define print_enum(array, val, label, unknown) \ + __print_enum(array, array ## _count, val, label, unknown) + +DECLARE_ENUM_NAMES(duplex); +DECLARE_ENUM_NAMES(port); +DECLARE_ENUM_NAMES(transceiver); + +void print_flags(const struct flag_info *info, unsigned int n_info, u32 value); +void __print_enum(const char *const *info, unsigned int n_info, + unsigned int val, const char *label, const char *unknown); +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 0edb80b19b9d..bc7969f829f6 100644 --- a/ethtool.c +++ b/ethtool.c @@ -30,6 +30,7 @@ */ #include "internal.h" +#include "common.h" #include <string.h> #include <stdlib.h> #include <sys/stat.h> @@ -56,26 +57,6 @@ #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 @@ -123,29 +104,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; @@ -428,26 +386,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; @@ -853,31 +791,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; } @@ -946,56 +862,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'; - } 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; @@ -2738,8 +2604,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) { @@ -2785,13 +2650,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, @@ -5057,6 +4922,7 @@ static int show_usage(struct cmd_context *ctx); * support so that we do not get unresolved symbols in args array below */ #define nl_gdrv NULL +#define nl_gset NULL #endif static const struct option { @@ -5381,6 +5247,7 @@ int main(int argc, char **argp) } if ((*argp)[0] == '-') exit_bad_args(); + nl_func = nl_gset; func = do_gset; want_device = 1; diff --git a/netlink/extapi.h b/netlink/extapi.h index 546090a02a0d..20c9b03b2d3c 100644 --- a/netlink/extapi.h +++ b/netlink/extapi.h @@ -14,6 +14,7 @@ int netlink_init(struct cmd_context *ctx); int netlink_done(struct cmd_context *ctx); int nl_gdrv(struct cmd_context *ctx); +int nl_gset(struct cmd_context *ctx); int nl_monitor(struct cmd_context *ctx); void monitor_usage(); diff --git a/netlink/monitor.c b/netlink/monitor.c index ae7d6080e03d..32d842611011 100644 --- a/netlink/monitor.c +++ b/netlink/monitor.c @@ -67,6 +67,7 @@ static int monitor_event_cb(const struct nlmsghdr *nlhdr, void *data) } int drvinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int settings_reply_cb(const struct nlmsghdr *nlhdr, void *data); static struct { uint8_t cmd; @@ -80,6 +81,10 @@ static struct { .cmd = ETHNL_CMD_SET_DRVINFO, .cb = drvinfo_reply_cb, }, + { + .cmd = ETHNL_CMD_SET_SETTINGS, + .cb = settings_reply_cb, + }, }; static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data) @@ -113,6 +118,15 @@ static struct monitor_option monitor_opts[] = { .pattern = "-i|--driver", .cmd = ETHNL_CMD_SET_DRVINFO, }, + { + .pattern = "|-s|--change", + .cmd = ETHNL_CMD_SET_SETTINGS, + .info_mask = ETH_SETTINGS_IM_LINKINFO | + ETH_SETTINGS_IM_LINKMODES | + ETH_SETTINGS_IM_MSGLEVEL | + ETH_SETTINGS_IM_WOLINFO | + ETH_SETTINGS_IM_LINK, + }, }; static bool pattern_match(const char *s, const char *pattern) diff --git a/netlink/netlink.c b/netlink/netlink.c index 69f692d2cf04..fc87577191f0 100644 --- a/netlink/netlink.c +++ b/netlink/netlink.c @@ -4,8 +4,10 @@ #include <errno.h> #include "../internal.h" +#include "../common.h" #include "netlink.h" #include "extapi.h" +#include "strset.h" /* misc helpers */ @@ -352,6 +354,124 @@ const char *get_dev_name(const struct nlattr *nest) return mnl_attr_get_str(dev_tb[ETHA_DEV_NAME]); } +int dump_link_modes(const struct nlattr *bitset, bool mask, unsigned class, + const char *before, const char *between, const char *after, + const char *if_none) +{ + const struct nlattr *bitset_tb[ETHA_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[ETHA_BITSET_BITS]; + if (ret < 0) + goto err_nonl; + if (!bits) { + const struct stringset *lm_strings = + global_stringset(ETH_SS_LINK_MODES); + unsigned int count; + unsigned int idx; + const char *name; + + bits = mask ? bitset_tb[ETHA_BITSET_MASK] : + bitset_tb[ETHA_BITSET_VALUE]; + ret = -EFAULT; + if (!bits || !bitset_tb[ETHA_BITSET_SIZE]) + goto err_nonl; + count = mnl_attr_get_u32(bitset_tb[ETHA_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), "b%d", 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[ETHA_BIT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + unsigned int idx; + const char *name; + + if (mnl_attr_get_type(bit) != ETHA_BITS_BIT) + continue; + ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info); + if (ret < 0) + goto err; + ret = -EFAULT; + if (!tb[ETHA_BIT_INDEX] || !tb[ETHA_BIT_NAME]) + goto err; + if (!mask && !tb[ETHA_BIT_VALUE]) + continue; + + idx = mnl_attr_get_u32(tb[ETHA_BIT_INDEX]); + name = mnl_attr_get_str(tb[ETHA_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; +} + /* request helpers */ int ethnl_prep_get_request(struct cmd_context *ctx, unsigned int nlcmd, diff --git a/netlink/netlink.h b/netlink/netlink.h index 15bf9f3873b0..a572a165718e 100644 --- a/netlink/netlink.h +++ b/netlink/netlink.h @@ -58,6 +58,10 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx, int *retptr); bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr); +int dump_link_modes(const struct nlattr *bitset, bool mask, unsigned class, + const char *before, const char *between, const char *after, + const char *if_none); + int msg_init(struct nl_context *nlctx, int cmd, unsigned int flags); int ethnl_process_reply(struct nl_context *nlctx, mnl_cb_t reply_cb); int attr_cb(const struct nlattr *attr, void *data); diff --git a/netlink/settings.c b/netlink/settings.c new file mode 100644 index 000000000000..120fc95aad19 --- /dev/null +++ b/netlink/settings.c @@ -0,0 +1,301 @@ +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" + +/* GET_SETTINGS */ + +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; +} + +int settings_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHA_SETTINGS_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool allfail = true; + bool first = true; + int port = -1; + int ret; + + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + nlctx->devname = get_dev_name(tb[ETHA_SETTINGS_DEV]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (tb[ETHA_SETTINGS_LINK_MODES] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKMODES)) { + const struct nlattr *attr = tb[ETHA_SETTINGS_LINK_MODES]; + bool autoneg; + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + ret = dump_link_modes(attr, true, LM_CLASS_PORT, + "Supported ports: [ ", " ", " ]\n", NULL); + if (ret < 0) + return ret; + ret = dump_link_modes(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(attr, true, LM_CLASS_FEC, + "Supported FEC modes: ", " ", "\n", + "No"); + if (ret < 0) + return ret; + + ret = dump_link_modes(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"); + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + ret = dump_link_modes(attr, true, LM_CLASS_FEC, + "Advertised FEC modes: ", " ", "\n", + "No"); + if (ret < 0) + return ret; + } + if (tb[ETHA_SETTINGS_PEER_MODES] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKMODES)) { + const struct nlattr *attr = tb[ETHA_SETTINGS_PEER_MODES]; + bool autoneg; + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + ret = dump_link_modes(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(attr, true, LM_CLASS_FEC, + "Link partner advertised FEC modes: ", + " ", "\n", "No"); + if (ret < 0) + return ret; + } + if (tb[ETHA_SETTINGS_SPEED] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) { + u32 val = mnl_attr_get_u32(tb[ETHA_SETTINGS_SPEED]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + if (val == 0 || val == (u16)(-1) || val == (u32)(-1)) + printf("\tSpeed: Unknown!\n"); + else + printf("\tSpeed: %uMb/s\n", val); + } + if (tb[ETHA_SETTINGS_DUPLEX] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) { + u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_DUPLEX]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + print_enum(names_duplex, val, "\tDuplex: ", "Unknown! (%i)"); + } + if (tb[ETHA_SETTINGS_PORT] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) { + u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_PORT]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + print_enum(names_port, val, "\tPort: ", "Unknown! (%i)\n"); + port = val; + } + if (tb[ETHA_SETTINGS_PHYADDR] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) { + u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_PHYADDR]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + printf("\tPHYAD: %u\n", val); + } + if (tb[ETHA_SETTINGS_TRANSCEIVER] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) { + u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_TRANSCEIVER]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + print_enum(names_transceiver, val, "\tTransceiver: ", + "Unknown!"); + } + if (tb[ETHA_SETTINGS_AUTONEG] && + mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) { + u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_AUTONEG]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + printf("\tAuto-negotiation: %s\n", + (val == AUTONEG_DISABLE) ? "off" : "on"); + } + if (tb[ETHA_SETTINGS_TP_MDIX] && tb[ETHA_SETTINGS_TP_MDIX] && + port == PORT_TP && mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) { + u8 mdix = mnl_attr_get_u8(tb[ETHA_SETTINGS_TP_MDIX]); + u8 mdix_ctrl = mnl_attr_get_u8(tb[ETHA_SETTINGS_TP_MDIX_CTRL]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + dump_mdix(mdix, mdix_ctrl); + } + if (tb[ETHA_SETTINGS_WOL_MODES] && + mask_ok(nlctx, ETH_SETTINGS_IM_WOLINFO)) { + struct ethtool_wolinfo wolinfo = {}; + const struct nla_bitfield32 *wol_bf = + mnl_attr_get_payload(tb[ETHA_SETTINGS_WOL_MODES]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + wolinfo.wolopts = wol_bf->value; + wolinfo.supported = wol_bf->selector; + if (tb[ETHA_SETTINGS_SOPASS]) + nl_copy_payload(wolinfo.sopass, SOPASS_MAX, + tb[ETHA_SETTINGS_SOPASS]); + ret = dump_wol(&wolinfo); + if (ret) + return ret; + allfail = false; + } + if (tb[ETHA_SETTINGS_MSGLVL] && + mask_ok(nlctx, ETH_SETTINGS_IM_MSGLEVEL)) { + struct nla_bitfield32 *val = + mnl_attr_get_payload(tb[ETHA_SETTINGS_MSGLVL]); + u32 msglvl = val->value; + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + printf(" Current message level: 0x%08x (%d)\n" + " ", + msglvl, msglvl); + print_flags(flags_msglvl, n_flags_msglvl, msglvl); + fputc('\n', stdout); + allfail = false; + } + if (tb[ETHA_SETTINGS_LINK] && mask_ok(nlctx, ETH_SETTINGS_IM_LINK)) { + u32 link = mnl_attr_get_u32(tb[ETHA_SETTINGS_LINK]); + + if (first) { + printf("Settings for %s:\n", nlctx->devname); + first = false; + } + printf("\tLink detected: %s\n", link ? "yes" : "no"); + allfail = false; + }; + + if (allfail && !nlctx->is_monitor) { + fputs("No data available\n", stdout); + nlctx->exit_code = 75; + return MNL_CB_ERROR; + } + return MNL_CB_OK; +} + +int settings_request(struct cmd_context *ctx, uint32_t info_mask) +{ + bool compact = info_mask & ETH_SETTINGS_IM_FEATURES; + struct nl_context *nlctx = ctx->nlctx; + int ret; + + if (compact) + load_global_strings(nlctx); + + ret = ethnl_prep_get_request(ctx, ETHNL_CMD_GET_SETTINGS, + ETHA_SETTINGS_DEV); + if (ret < 0) + return ret; + if (ethnla_put_u32(nlctx, ETHA_SETTINGS_INFOMASK, info_mask) || + ethnla_put_flag(nlctx, ETHA_SETTINGS_COMPACT, compact)) + return -EMSGSIZE; + return ethnl_send_get_request(nlctx, settings_reply_cb); +} + +int nl_gset(struct cmd_context *ctx) +{ + return settings_request(ctx, ETH_SETTINGS_IM_LINKINFO | + ETH_SETTINGS_IM_LINKMODES | + ETH_SETTINGS_IM_MSGLEVEL | + ETH_SETTINGS_IM_WOLINFO | + ETH_SETTINGS_IM_LINK); +} + +} -- 2.18.0
| |