summaryrefslogtreecommitdiffstats
path: root/netlink/tunnels.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netlink/tunnels.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/netlink/tunnels.c b/netlink/tunnels.c
new file mode 100644
index 0000000..b464046
--- /dev/null
+++ b/netlink/tunnels.c
@@ -0,0 +1,236 @@
+/*
+ * tunnel.c - device tunnel information
+ *
+ * Implementation of "ethtool --show-tunnels <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#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);
+}