From patchwork Thu Sep 10 19:09:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Kicinski X-Patchwork-Id: 261126 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.1 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B5C1AC433E2 for ; Thu, 10 Sep 2020 19:10:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 711A421556 for ; Thu, 10 Sep 2020 19:10:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1599765029; bh=ByRDj3GRVYsU7e6aJGXifFwgRun7FljVRICiS2WXhtg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=y/L5MFNaf3py820uetHiuB6cKPGzBMS3e2HiwuKA9ou3pE/yc74767Xr5NrHeQ/e0 /xP5E2NkVEuOS3lhK3mUUP6i+PnHE+SX60Iho+/DdUpPfYJkcVAFgJrmidnoBURy2g 4WQxH1DdFe7wwabztZCnqh/YOPwpL+lvTNPjHbWk= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727792AbgIJTK0 (ORCPT ); Thu, 10 Sep 2020 15:10:26 -0400 Received: from mail.kernel.org ([198.145.29.99]:39604 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726837AbgIJTJY (ORCPT ); Thu, 10 Sep 2020 15:09:24 -0400 Received: from kicinski-fedora-PC1C0HJN.thefacebook.com (unknown [163.114.132.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id E45EA21D7E; Thu, 10 Sep 2020 19:09:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1599764963; bh=ByRDj3GRVYsU7e6aJGXifFwgRun7FljVRICiS2WXhtg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Q71VtKKRFcNQtzQBcZLckaauW6pbqIdeaW3rrlMKOUhA1vEorPk7BqCb9+qKNkMla QnquYPmVfXzTjnysEecRaMRcFx0HKAXg5Q3l+lLMtfbhoNW4oc04ZPQxFHzGJhC4Hy kVWXVT4eEf2oTNBHZJAoPMx/S6Ml9rdjlQOWhCtM= From: Jakub Kicinski To: mkubecek@suse.cz Cc: netdev@vger.kernel.org, Jakub Kicinski Subject: [PATCH ethtool v2 2/2] tunnels: implement new --show-tunnels command Date: Thu, 10 Sep 2020 12:09:15 -0700 Message-Id: <20200910190915.564904-3-kuba@kernel.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200910190915.564904-1-kuba@kernel.org> References: <20200910190915.564904-1-kuba@kernel.org> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add support for the new show-tunnels command. Support dump. # ethtool --show-tunnels \* Tunnel information for eth0: UDP port table 0: Size: 4 Types: vxlan No entries UDP port table 1: Size: 4 Types: geneve, vxlan-gpe Entries (2): port 1230, vxlan-gpe port 6081, geneve v2: htons -> ntohs Signed-off-by: Jakub Kicinski --- Makefile.am | 2 +- ethtool.8.in | 9 ++ ethtool.c | 5 + netlink/extapi.h | 2 + netlink/tunnels.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 netlink/tunnels.c diff --git a/Makefile.am b/Makefile.am index aca0ad7bc773..0e237d070d4b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,7 @@ ethtool_SOURCES += \ netlink/channels.c netlink/coalesce.c netlink/pause.c \ netlink/eee.c netlink/tsinfo.c \ netlink/desc-ethtool.c netlink/desc-genlctrl.c \ - netlink/desc-rtnl.c netlink/cable_test.c \ + netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.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 a50a4769895c..42c4767db33e 100644 --- a/ethtool.8.in +++ b/ethtool.8.in @@ -459,6 +459,9 @@ ethtool \- query or control network driver and hardware settings .BN last N .BN step N .BN pair N +.HP +.B ethtool \-\-show\-tunnels +.I devname . .\" Adjust lines (i.e. full justification) and hyphenate. .ad @@ -1383,6 +1386,12 @@ shown. If a device name is used as argument, only notification for this device are shown. Default is to show notifications for all devices. .RE +.TP +.B \-\-show\-tunnels +Show tunnel-related device capabilities and state. +List UDP ports kernel has programmed the device to parse as VxLAN, +or GENEVE tunnels. +.RE .SH BUGS Not supported (in part or whole) on all network drivers. .SH AUTHOR diff --git a/ethtool.c b/ethtool.c index 606af3e6b48f..23ecfcfd069c 100644 --- a/ethtool.c +++ b/ethtool.c @@ -5919,6 +5919,11 @@ static const struct option args[] = { " [ step N ]\n" " [ pair N ]\n" }, + { + .opts = "--show-tunnels", + .nlfunc = nl_gtunnels, + .help = "Show NIC tunnel offload information", + }, { .opts = "-h|--help", .no_dev = true, diff --git a/netlink/extapi.h b/netlink/extapi.h index a35d5f2c0b26..9285e2325dfd 100644 --- a/netlink/extapi.h +++ b/netlink/extapi.h @@ -37,6 +37,7 @@ int nl_seee(struct cmd_context *ctx); int nl_tsinfo(struct cmd_context *ctx); int nl_cable_test(struct cmd_context *ctx); int nl_cable_test_tdr(struct cmd_context *ctx); +int nl_gtunnels(struct cmd_context *ctx); int nl_monitor(struct cmd_context *ctx); void nl_monitor_usage(void); @@ -84,6 +85,7 @@ static inline void nl_monitor_usage(void) #define nl_tsinfo NULL #define nl_cable_test NULL #define nl_cable_test_tdr NULL +#define nl_gtunnels NULL #endif /* ETHTOOL_ENABLE_NETLINK */ diff --git a/netlink/tunnels.c b/netlink/tunnels.c new file mode 100644 index 000000000000..b464046c021e --- /dev/null +++ b/netlink/tunnels.c @@ -0,0 +1,236 @@ +/* + * tunnel.c - device tunnel information + * + * Implementation of "ethtool --show-tunnels " + */ + +#include +#include +#include +#include + +#include "../common.h" +#include "../internal.h" + +#include "bitset.h" +#include "netlink.h" +#include "strset.h" + +/* TUNNEL_INFO_GET */ + +static int +tunnel_info_strings_load(struct nl_context *nlctx, + const struct stringset **strings) +{ + int ret; + + if (*strings) + return 0; + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return ret; + *strings = global_stringset(ETH_SS_UDP_TUNNEL_TYPES, + nlctx->ethnl2_socket); + return 0; +} + +static int +tunnel_info_dump_udp_entry(struct nl_context *nlctx, const struct nlattr *entry, + const struct stringset **strings) +{ + const struct nlattr *entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(entry_tb); + const struct nlattr *attr; + unsigned int port, type; + int ret; + + if (tunnel_info_strings_load(nlctx, strings)) + return 1; + + ret = mnl_attr_parse_nested(entry, attr_cb, &entry_tb_info); + if (ret < 0) { + fprintf(stderr, "malformed netlink message (udp entry)\n"); + return 1; + } + + attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT]; + if (!attr || mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + fprintf(stderr, "malformed netlink message (port)\n"); + return 1; + } + port = ntohs(mnl_attr_get_u16(attr)); + + attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE]; + if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + fprintf(stderr, "malformed netlink message (tunnel type)\n"); + return 1; + } + type = mnl_attr_get_u32(attr); + + printf(" port %d, %s\n", port, get_string(*strings, type)); + + return 0; +} + +static void tunnel_info_dump_cb(unsigned int idx, const char *name, bool val, + void *data) +{ + bool *first = data; + + if (!val) + return; + + if (!*first) + putchar(','); + *first = false; + + if (name) + printf(" %s", name); + else + printf(" bit%u", idx); +} + +static int +tunnel_info_dump_list(struct nl_context *nlctx, const struct nlattr *attr, + const struct stringset **strings) +{ + bool first = true; + int ret; + + if (bitset_is_empty(attr, false, &ret) || ret) { + if (ret) + return 1; + + printf(" Types: none (static entries)\n"); + return 0; + } + + if (bitset_is_compact(attr) && + tunnel_info_strings_load(nlctx, strings)) + return 1; + + printf(" Types:"); + ret = walk_bitset(attr, *strings, tunnel_info_dump_cb, &first); + putchar('\n'); + return ret; +} + +static int +tunnel_info_dump_udp_table(const struct nlattr *table, struct nl_context *nlctx, + const struct stringset **strings) +{ + const struct nlattr *table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(table_tb); + const struct nlattr *attr; + int i, ret; + + ret = mnl_attr_parse_nested(table, attr_cb, &table_tb_info); + if (ret < 0) { + fprintf(stderr, "malformed netlink message (udp table)\n"); + return 1; + } + + attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE]; + if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + fprintf(stderr, "malformed netlink message (table size)\n"); + return 1; + } + printf(" Size: %d\n", mnl_attr_get_u32(attr)); + + attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES]; + if (!attr || tunnel_info_dump_list(nlctx, attr, strings)) { + fprintf(stderr, "malformed netlink message (types)\n"); + return 1; + } + + if (!table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY]) { + printf(" No entries\n"); + return 0; + } + + i = 0; + mnl_attr_for_each_nested(attr, table) { + if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY) + i++; + } + + printf(" Entries (%d):\n", i++); + mnl_attr_for_each_nested(attr, table) { + if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY) + if (tunnel_info_dump_udp_entry(nlctx, attr, strings)) + return 1; + } + + return 0; +} + +static int +tunnel_info_dump_udp(const struct nlattr *udp_ports, struct nl_context *nlctx) +{ + const struct stringset *strings = NULL; + const struct nlattr *table; + int i, err; + + i = 0; + mnl_attr_for_each_nested(table, udp_ports) { + if (mnl_attr_get_type(table) != ETHTOOL_A_TUNNEL_UDP_TABLE) + continue; + + printf(" UDP port table %d: \n", i++); + err = tunnel_info_dump_udp_table(table, nlctx, &strings); + if (err) + return err; + } + + return 0; +} + +static int tunnel_info_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_TUNNEL_INFO_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + const struct nlattr *udp_ports; + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_TUNNEL_INFO_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + printf("Tunnel information for %s:\n", nlctx->devname); + + udp_ports = tb[ETHTOOL_A_TUNNEL_INFO_UDP_PORTS]; + if (udp_ports) + if (tunnel_info_dump_udp(udp_ports, nlctx)) + return err_ret; + + return MNL_CB_OK; +} + +int nl_gtunnels(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_TUNNEL_INFO_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TUNNEL_INFO_GET, + ETHTOOL_A_TUNNEL_INFO_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, tunnel_info_reply_cb); +}