summaryrefslogtreecommitdiffstats
path: root/tipc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:14:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:14:35 +0000
commit9b8a97db9ec4b795e29e72289005fbc58484ebeb (patch)
treee24ca2d68215e57b4759fe5c032629821eabb250 /tipc
parentInitial commit. (diff)
downloadiproute2-9b8a97db9ec4b795e29e72289005fbc58484ebeb.tar.xz
iproute2-9b8a97db9ec4b795e29e72289005fbc58484ebeb.zip
Adding upstream version 6.8.0.upstream/6.8.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tipc')
-rw-r--r--tipc/.gitignore1
-rw-r--r--tipc/Makefile24
-rw-r--r--tipc/README63
-rw-r--r--tipc/bearer.c1134
-rw-r--r--tipc/bearer.h22
-rw-r--r--tipc/cmdl.c130
-rw-r--r--tipc/cmdl.h54
-rw-r--r--tipc/link.c1248
-rw-r--r--tipc/link.h17
-rw-r--r--tipc/media.c273
-rw-r--r--tipc/media.h17
-rw-r--r--tipc/misc.c167
-rw-r--r--tipc/misc.h19
-rw-r--r--tipc/msg.c48
-rw-r--r--tipc/msg.h16
-rw-r--r--tipc/nametable.c118
-rw-r--r--tipc/nametable.h17
-rw-r--r--tipc/node.c505
-rw-r--r--tipc/node.h17
-rw-r--r--tipc/peer.c142
-rw-r--r--tipc/peer.h17
-rw-r--r--tipc/socket.c146
-rw-r--r--tipc/socket.h17
-rw-r--r--tipc/tipc.c130
24 files changed, 4342 insertions, 0 deletions
diff --git a/tipc/.gitignore b/tipc/.gitignore
new file mode 100644
index 0000000..39ed83d
--- /dev/null
+++ b/tipc/.gitignore
@@ -0,0 +1 @@
+tipc
diff --git a/tipc/Makefile b/tipc/Makefile
new file mode 100644
index 0000000..4f0aba7
--- /dev/null
+++ b/tipc/Makefile
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../config.mk
+
+TIPCOBJ=bearer.o \
+ cmdl.o link.o \
+ media.o misc.o \
+ msg.o nametable.o \
+ node.o socket.o \
+ peer.o tipc.o
+
+TARGETS += tipc
+
+all: $(TARGETS) $(LIBS)
+
+tipc: $(TIPCOBJ)
+ $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
+
+install: all
+ for i in $(TARGETS); \
+ do install -m 0755 $$i $(DESTDIR)$(SBINDIR); \
+ done
+
+clean:
+ rm -f $(TIPCOBJ) $(TARGETS)
diff --git a/tipc/README b/tipc/README
new file mode 100644
index 0000000..529d781
--- /dev/null
+++ b/tipc/README
@@ -0,0 +1,63 @@
+DESIGN DECISIONS
+----------------
+
+HELP
+~~~~
+--help or -h is used for help. We do not reserve the bare word "help", which
+for example the ip command does. Reserving a bare word like help quickly
+becomes cumbersome to handle in the code. It might be simple to handle
+when it's passed early in the command chain like "ip addr help". But when
+the user tries to pass "help" further down this requires manual checks and
+special treatment. For example, at the time of writing this tool, it's
+possible to create a vlan named "help" with the ip tool, but it's impossible
+to remove it, the command just shows help. This is an effect of treating
+bare words specially.
+
+Help texts are not dynamically generated. That is, we do not pass data structures
+like command list or option lists and print them dynamically. This is
+intentional. There is always that exception and when it comes to help texts
+these exceptions are normally neglected at the expense of usability.
+
+KEY-VALUE
+~~~~~~~~~
+All options are key-values. There are both drawbacks and benefits to this.
+The main drawback is that it becomes more to write for the user and
+information might seem redundant. The main benefits is scalability and code
+simplification. Consistency is important.
+
+Consider this.
+1. tipc link set priority PRIO link LINK
+2. tipc link set LINK priority PRIO
+
+Link might seem redundant in (1). However, if the command should live for many
+years and be able to evolve example (2) limits the set command to only work on a
+single link with no ability to extend. As an example, lets say we introduce
+grouping on the kernel side.
+
+1. tipc link set priority PRIO group GROUP
+2. tipc link set ??? priority PRIO group GROUP
+
+2. breaks, we can't extend the command to cover a group.
+
+PARSING
+~~~~~~~
+Commands are single words. As an example, all words in "tipc link list" are
+commands. Options are key-values that can be given in any order. In
+"tipc link set priority PRIO link LINK" "tipc link set" are commands while
+priority and link are options. Meaning that they can be given like
+"tipc link set link LINK priority PRIO".
+
+Abbreviation matching works for both command and options. Meaning that
+"tipc link set priority PRIO link LINK" could be given as
+"tipc l s p PRIO l LINK" and "tipc link list" as "tipc l l".
+
+MEMORY
+~~~~~~
+The tool strives to avoid allocating memory on the heap. Most (if not all)
+memory allocations are on the stack.
+
+RETURNING
+~~~~~~~~~
+The tool could throw exit() deep down in functions but doing so always seems
+to limit the program in the long run. So we output the error and return an
+appropriate error code upon failure.
diff --git a/tipc/bearer.c b/tipc/bearer.c
new file mode 100644
index 0000000..bb434f5
--- /dev/null
+++ b/tipc/bearer.c
@@ -0,0 +1,1134 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * bearer.c TIPC bearer functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <linux/tipc_netlink.h>
+#include <linux/tipc.h>
+#include <linux/genetlink.h>
+#include <linux/if.h>
+#include <libmnl/libmnl.h>
+
+#include "mnl_utils.h"
+#include "utils.h"
+#include "cmdl.h"
+#include "msg.h"
+#include "bearer.h"
+
+#define UDP_PROP_IP 1
+#define UDP_PROP_PORT 2
+
+struct cb_data {
+ int attr;
+ int prop;
+ struct nlmsghdr *nlh;
+};
+
+static void _print_bearer_opts(void)
+{
+ fprintf(stderr,
+ "OPTIONS\n"
+ " priority - Bearer link priority\n"
+ " tolerance - Bearer link tolerance\n"
+ " window - Bearer link window\n"
+ " mtu - Bearer link mtu\n");
+}
+
+void print_bearer_media(void)
+{
+ fprintf(stderr,
+ "\nMEDIA\n"
+ " udp - User Datagram Protocol\n"
+ " ib - Infiniband\n"
+ " eth - Ethernet\n");
+}
+
+static void cmd_bearer_enable_l2_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr,
+ "Usage: %s bearer enable media %s device DEVICE [OPTIONS]\n"
+ "\nOPTIONS\n"
+ " domain DOMAIN - Discovery domain\n"
+ " priority PRIORITY - Bearer priority\n",
+ cmdl->argv[0], media);
+}
+
+static void cmd_bearer_enable_udp_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr,
+ "Usage: %s bearer enable [OPTIONS] media %s name NAME [localip IP|device DEVICE] [UDP OPTIONS]\n\n"
+ "OPTIONS\n"
+ " domain DOMAIN - Discovery domain\n"
+ " priority PRIORITY - Bearer priority\n\n"
+ "UDP OPTIONS\n"
+ " localport PORT - Local UDP port (default 6118)\n"
+ " remoteip IP - Remote IP address\n"
+ " remoteport PORT - Remote UDP port (default 6118)\n",
+ cmdl->argv[0], media);
+}
+
+static int get_netid_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_NET_MAX + 1] = {};
+ int *netid = (int*)data;
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_NET])
+ return MNL_CB_ERROR;
+ mnl_attr_parse_nested(info[TIPC_NLA_NET], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_NET_ID])
+ return MNL_CB_ERROR;
+ *netid = mnl_attr_get_u32(attrs[TIPC_NLA_NET_ID]);
+
+ return MNL_CB_OK;
+}
+
+static int generate_multicast(short af, char *buf, int bufsize)
+{
+ struct mnlu_gen_socket bearer_nlg;
+ struct nlmsghdr *nlh;
+ int netid;
+ int err = 0;
+
+ err = mnlu_gen_socket_open(&bearer_nlg, TIPC_GENL_V2_NAME,
+ TIPC_GENL_V2_VERSION);
+ if (err)
+ return -1;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&bearer_nlg, TIPC_NL_NET_GET,
+ NLM_F_REQUEST | NLM_F_DUMP);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialization failed\n");
+ mnlu_gen_socket_close(&bearer_nlg);
+ return -1;
+ }
+
+ err = mnlu_gen_socket_sndrcv(&bearer_nlg, nlh, get_netid_cb, &netid);
+ if (err) {
+ fprintf(stderr, "error, failed to fetch TIPC network id from kernel\n");
+ mnlu_gen_socket_close(&bearer_nlg);
+ return -EINVAL;
+ }
+ if (af == AF_INET)
+ snprintf(buf, bufsize, "228.0.%u.%u", (netid>>8) & 0xFF, netid & 0xFF);
+ else
+ snprintf(buf, bufsize, "ff02::%u", netid);
+
+ mnlu_gen_socket_close(&bearer_nlg);
+ return 0;
+}
+
+static struct ifreq ifr;
+static int nl_dump_req_filter(struct nlmsghdr *nlh, int reqlen)
+{
+ struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
+
+ ifa->ifa_index = ifr.ifr_ifindex;
+
+ return 0;
+}
+
+static int nl_dump_addr_filter(struct nlmsghdr *nlh, void *arg)
+{
+ struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
+ char *r_addr = (char *)arg;
+ int len = nlh->nlmsg_len;
+ struct rtattr *addr_attr;
+
+ if (ifr.ifr_ifindex != ifa->ifa_index)
+ return 0;
+
+ if (strlen(r_addr) > 0)
+ return 0;
+
+ addr_attr = parse_rtattr_one(IFA_ADDRESS, IFA_RTA(ifa),
+ len - NLMSG_LENGTH(sizeof(*ifa)));
+ if (!addr_attr)
+ return 0;
+
+ if (ifa->ifa_family == AF_INET) {
+ struct sockaddr_in ip4addr;
+ memcpy(&ip4addr.sin_addr, RTA_DATA(addr_attr),
+ sizeof(struct in_addr));
+ inet_ntop(AF_INET, &ip4addr.sin_addr, r_addr,
+ INET_ADDRSTRLEN);
+ } else if (ifa->ifa_family == AF_INET6) {
+ struct sockaddr_in6 ip6addr;
+ memcpy(&ip6addr.sin6_addr, RTA_DATA(addr_attr),
+ sizeof(struct in6_addr));
+ inet_ntop(AF_INET6, &ip6addr.sin6_addr, r_addr,
+ INET6_ADDRSTRLEN);
+ }
+ return 0;
+}
+
+static int cmd_bearer_validate_and_get_addr(const char *name, char *r_addr)
+{
+ struct rtnl_handle rth = { .fd = -1 };
+ int err = -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ if (!name || !r_addr || get_ifname(ifr.ifr_name, name))
+ return err;
+
+ ifr.ifr_ifindex = ll_name_to_index(ifr.ifr_name);
+ if (!ifr.ifr_ifindex)
+ return err;
+
+ /* remove from cache */
+ ll_drop_by_index(ifr.ifr_ifindex);
+
+ if ((err = rtnl_open(&rth, 0)) < 0)
+ return err;
+
+ if ((err = rtnl_addrdump_req(&rth, AF_UNSPEC, nl_dump_req_filter)) > 0)
+ err = rtnl_dump_filter(&rth, nl_dump_addr_filter, r_addr);
+
+ rtnl_close(&rth);
+ return err;
+}
+
+static int nl_add_udp_enable_opts(struct nlmsghdr *nlh, struct opt *opts,
+ struct cmdl *cmdl)
+{
+ int err;
+ struct opt *opt;
+ struct nlattr *nest;
+ char buf[INET6_ADDRSTRLEN];
+ char *locport = "6118";
+ char *remport = "6118";
+ char *locip = NULL;
+ char *remip = NULL;
+ struct addrinfo *loc = NULL;
+ struct addrinfo *rem = NULL;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM
+ };
+ char addr[INET6_ADDRSTRLEN] = {0};
+
+ opt = get_opt(opts, "device");
+ if (opt && cmd_bearer_validate_and_get_addr(opt->val, addr) < 0) {
+ fprintf(stderr, "error, no device name available\n");
+ return -EINVAL;
+ }
+
+ if (strlen(addr) > 0) {
+ locip = addr;
+ } else {
+ opt = get_opt(opts, "localip");
+ if (!opt) {
+ fprintf(stderr, "error, udp bearer localip/device missing\n");
+ cmd_bearer_enable_udp_help(cmdl, "udp");
+ return -EINVAL;
+ }
+ locip = opt->val;
+ }
+
+ if ((opt = get_opt(opts, "remoteip")))
+ remip = opt->val;
+
+ if ((opt = get_opt(opts, "localport")))
+ locport = opt->val;
+
+ if ((opt = get_opt(opts, "remoteport")))
+ remport = opt->val;
+
+ if ((err = getaddrinfo(locip, locport, &hints, &loc))) {
+ fprintf(stderr, "UDP local address error: %s\n",
+ gai_strerror(err));
+ return err;
+ }
+
+ if (!remip) {
+ if (generate_multicast(loc->ai_family, buf, sizeof(buf))) {
+ fprintf(stderr, "Failed to generate multicast address\n");
+ freeaddrinfo(loc);
+ return -EINVAL;
+ }
+ remip = buf;
+ }
+
+ if ((err = getaddrinfo(remip, remport, &hints, &rem))) {
+ fprintf(stderr, "UDP remote address error: %s\n",
+ gai_strerror(err));
+ freeaddrinfo(loc);
+ return err;
+ }
+
+ if (rem->ai_family != loc->ai_family) {
+ fprintf(stderr, "UDP local and remote AF mismatch\n");
+ freeaddrinfo(rem);
+ freeaddrinfo(loc);
+ return -EINVAL;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_UDP_OPTS);
+ mnl_attr_put(nlh, TIPC_NLA_UDP_LOCAL, loc->ai_addrlen, loc->ai_addr);
+ mnl_attr_put(nlh, TIPC_NLA_UDP_REMOTE, rem->ai_addrlen, rem->ai_addr);
+ mnl_attr_nest_end(nlh, nest);
+
+ freeaddrinfo(rem);
+ freeaddrinfo(loc);
+
+ return 0;
+}
+
+static char *cmd_get_media_type(const struct cmd *cmd, struct cmdl *cmdl,
+ struct opt *opts)
+{
+ struct opt *opt = get_opt(opts, "media");
+
+ if (!opt) {
+ if (help_flag)
+ (cmd->help)(cmdl);
+ else
+ fprintf(stderr, "error, missing bearer media\n");
+ return NULL;
+ }
+ return opt->val;
+}
+
+static int nl_add_bearer_name(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, struct opt *opts,
+ const struct tipc_sup_media *sup_media)
+{
+ char bname[TIPC_MAX_BEARER_NAME];
+ int err;
+
+ if ((err = cmd_get_unique_bearer_name(cmd, cmdl, opts, bname, sup_media)))
+ return err;
+
+ mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, bname);
+ return 0;
+}
+
+int cmd_get_unique_bearer_name(const struct cmd *cmd, struct cmdl *cmdl,
+ struct opt *opts, char *bname,
+ const struct tipc_sup_media *sup_media)
+{
+ char *media;
+ char *identifier;
+ struct opt *opt;
+ const struct tipc_sup_media *entry;
+
+ if (!(media = cmd_get_media_type(cmd, cmdl, opts)))
+ return -EINVAL;
+
+ for (entry = sup_media; entry->media; entry++) {
+ if (strcmp(entry->media, media))
+ continue;
+
+ if (!(opt = get_opt(opts, entry->identifier))) {
+ if (help_flag)
+ (entry->help)(cmdl, media);
+ else
+ fprintf(stderr, "error, missing bearer %s\n",
+ entry->identifier);
+ return -EINVAL;
+ }
+
+ identifier = opt->val;
+ snprintf(bname, TIPC_MAX_BEARER_NAME, "%s:%s", media, identifier);
+
+ return 0;
+ }
+
+ fprintf(stderr, "error, invalid media type %s\n", media);
+
+ return -EINVAL;
+}
+
+static void cmd_bearer_add_udp_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr, "Usage: %s bearer add media %s name NAME remoteip REMOTEIP\n\n",
+ cmdl->argv[0], media);
+}
+
+static void cmd_bearer_add_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s bearer add media udp name NAME remoteip REMOTEIP\n",
+ cmdl->argv[0]);
+}
+
+static int udp_bearer_add(struct nlmsghdr *nlh, struct opt *opts,
+ struct cmdl *cmdl)
+{
+ int err;
+ struct opt *opt;
+ struct nlattr *opts_nest;
+ char *remport = "6118";
+
+ opts_nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_UDP_OPTS);
+
+ if ((opt = get_opt(opts, "remoteport")))
+ remport = opt->val;
+
+ if ((opt = get_opt(opts, "remoteip"))) {
+ char *ip = opt->val;
+ struct addrinfo *addr = NULL;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM
+ };
+
+ if ((err = getaddrinfo(ip, remport, &hints, &addr))) {
+ fprintf(stderr, "UDP address error: %s\n",
+ gai_strerror(err));
+ freeaddrinfo(addr);
+ return err;
+ }
+
+ mnl_attr_put(nlh, TIPC_NLA_UDP_REMOTE, addr->ai_addrlen,
+ addr->ai_addr);
+ freeaddrinfo(addr);
+ } else {
+ fprintf(stderr, "error, missing remoteip\n");
+ return -EINVAL;
+ }
+ mnl_attr_nest_end(nlh, opts_nest);
+
+ return 0;
+}
+
+static int cmd_bearer_add_media(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err;
+ char *media;
+ struct opt *opt;
+ struct nlattr *attrs;
+ struct opt opts[] = {
+ { "remoteip", OPT_KEYVAL, NULL },
+ { "remoteport", OPT_KEYVAL, NULL },
+ { "name", OPT_KEYVAL, NULL },
+ { "media", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ const struct tipc_sup_media sup_media[] = {
+ { "udp", "name", cmd_bearer_add_udp_help},
+ { NULL, },
+ };
+
+ /* Rewind optind to include media in the option list */
+ cmdl->optind--;
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ if (!(opt = get_opt(opts, "media"))) {
+ fprintf(stderr, "error, missing media value\n");
+ return -EINVAL;
+ }
+ media = opt->val;
+
+ if (strcmp(media, "udp") != 0) {
+ fprintf(stderr, "error, no \"%s\" media specific options available\n",
+ media);
+ return -EINVAL;
+ }
+ if (!(opt = get_opt(opts, "name"))) {
+ fprintf(stderr, "error, missing media name\n");
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_BEARER_ADD);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER);
+ err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media);
+ if (err)
+ return err;
+
+ err = udp_bearer_add(nlh, opts, cmdl);
+ if (err)
+ return err;
+
+ mnl_attr_nest_end(nlh, attrs);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int cmd_bearer_add(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "media", cmd_bearer_add_media, cmd_bearer_add_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_bearer_enable_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s bearer enable [OPTIONS] media MEDIA ARGS...\n\n"
+ "OPTIONS\n"
+ " domain DOMAIN - Discovery domain\n"
+ " priority PRIORITY - Bearer priority\n",
+ cmdl->argv[0]);
+ print_bearer_media();
+}
+
+static int cmd_bearer_enable(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err;
+ struct opt *opt;
+ struct nlattr *nest;
+ struct opt opts[] = {
+ { "device", OPT_KEYVAL, NULL },
+ { "domain", OPT_KEYVAL, NULL },
+ { "localip", OPT_KEYVAL, NULL },
+ { "localport", OPT_KEYVAL, NULL },
+ { "media", OPT_KEYVAL, NULL },
+ { "name", OPT_KEYVAL, NULL },
+ { "priority", OPT_KEYVAL, NULL },
+ { "remoteip", OPT_KEYVAL, NULL },
+ { "remoteport", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct tipc_sup_media sup_media[] = {
+ { "udp", "name", cmd_bearer_enable_udp_help},
+ { "eth", "device", cmd_bearer_enable_l2_help },
+ { "ib", "device", cmd_bearer_enable_l2_help },
+ { NULL, },
+ };
+
+ if (parse_opts(opts, cmdl) < 0) {
+ if (help_flag)
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_BEARER_ENABLE);
+ if (!nlh) {
+ fprintf(stderr, "error: message initialisation failed\n");
+ return -1;
+ }
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER);
+
+ if ((opt = get_opt(opts, "domain")))
+ mnl_attr_put_u32(nlh, TIPC_NLA_BEARER_DOMAIN, atoi(opt->val));
+
+ if ((opt = get_opt(opts, "priority"))) {
+ struct nlattr *props;
+
+ props = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_PROP);
+ mnl_attr_put_u32(nlh, TIPC_NLA_PROP_PRIO, atoi(opt->val));
+ mnl_attr_nest_end(nlh, props);
+ }
+
+ err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media);
+ if (err)
+ return err;
+
+ opt = get_opt(opts, "media");
+ if (opt && strcmp(opt->val, "udp") == 0) {
+ err = nl_add_udp_enable_opts(nlh, opts, cmdl);
+ if (err)
+ return err;
+ }
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static void cmd_bearer_disable_l2_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr, "Usage: %s bearer disable media %s device DEVICE\n",
+ cmdl->argv[0], media);
+}
+
+static void cmd_bearer_disable_udp_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr, "Usage: %s bearer disable media %s name NAME\n",
+ cmdl->argv[0], media);
+}
+
+static void cmd_bearer_disable_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s bearer disable media MEDIA ARGS...\n",
+ cmdl->argv[0]);
+ print_bearer_media();
+}
+
+static int cmd_bearer_disable(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err;
+ struct nlattr *nest;
+ struct opt opts[] = {
+ { "device", OPT_KEYVAL, NULL },
+ { "name", OPT_KEYVAL, NULL },
+ { "media", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct tipc_sup_media sup_media[] = {
+ { "udp", "name", cmd_bearer_disable_udp_help},
+ { "eth", "device", cmd_bearer_disable_l2_help },
+ { "ib", "device", cmd_bearer_disable_l2_help },
+ { NULL, },
+ };
+
+ if (parse_opts(opts, cmdl) < 0) {
+ if (help_flag)
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_BEARER_DISABLE);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER);
+ err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media);
+ if (err)
+ return err;
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, NULL, NULL);
+
+}
+
+static void cmd_bearer_set_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s bearer set OPTION media MEDIA ARGS...\n",
+ cmdl->argv[0]);
+ _print_bearer_opts();
+ print_bearer_media();
+}
+
+static void cmd_bearer_set_udp_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr, "Usage: %s bearer set OPTION media %s name NAME\n\n",
+ cmdl->argv[0], media);
+ _print_bearer_opts();
+}
+
+static void cmd_bearer_set_l2_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr,
+ "Usage: %s bearer set [OPTION]... media %s device DEVICE\n",
+ cmdl->argv[0], media);
+ _print_bearer_opts();
+}
+
+static int cmd_bearer_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err;
+ int val;
+ int prop;
+ struct nlattr *props;
+ struct nlattr *attrs;
+ struct opt opts[] = {
+ { "device", OPT_KEYVAL, NULL },
+ { "media", OPT_KEYVAL, NULL },
+ { "name", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct tipc_sup_media sup_media[] = {
+ { "udp", "name", cmd_bearer_set_udp_help},
+ { "eth", "device", cmd_bearer_set_l2_help },
+ { "ib", "device", cmd_bearer_set_l2_help },
+ { NULL, },
+ };
+
+ if (strcmp(cmd->cmd, "priority") == 0)
+ prop = TIPC_NLA_PROP_PRIO;
+ else if ((strcmp(cmd->cmd, "tolerance") == 0))
+ prop = TIPC_NLA_PROP_TOL;
+ else if ((strcmp(cmd->cmd, "window") == 0))
+ prop = TIPC_NLA_PROP_WIN;
+ else if ((strcmp(cmd->cmd, "mtu") == 0))
+ prop = TIPC_NLA_PROP_MTU;
+ else
+ return -EINVAL;
+
+ if (cmdl->optind >= cmdl->argc) {
+ fprintf(stderr, "error, missing value\n");
+ return -EINVAL;
+ }
+ val = atoi(shift_cmdl(cmdl));
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ if (prop == TIPC_NLA_PROP_MTU) {
+ char *media = cmd_get_media_type(cmd, cmdl, opts);
+
+ if (!media)
+ return -EINVAL;
+ else if (strcmp(media, "udp")) {
+ fprintf(stderr, "error, not supported for media\n");
+ return -EINVAL;
+ }
+ }
+
+ nlh = msg_init(TIPC_NL_BEARER_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER);
+
+ props = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_PROP);
+ mnl_attr_put_u32(nlh, prop, val);
+ mnl_attr_nest_end(nlh, props);
+
+ err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media);
+ if (err)
+ return err;
+
+ mnl_attr_nest_end(nlh, attrs);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int cmd_bearer_set(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "priority", cmd_bearer_set_prop, cmd_bearer_set_help },
+ { "tolerance", cmd_bearer_set_prop, cmd_bearer_set_help },
+ { "window", cmd_bearer_set_prop, cmd_bearer_set_help },
+ { "mtu", cmd_bearer_set_prop, cmd_bearer_set_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_bearer_get_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s bearer get [OPTION] media MEDIA ARGS...\n",
+ cmdl->argv[0]);
+ _print_bearer_opts();
+ print_bearer_media();
+}
+
+static void cmd_bearer_get_udp_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr, "Usage: %s bearer get [OPTION] media %s name NAME [UDP OPTIONS]\n\n",
+ cmdl->argv[0], media);
+ fprintf(stderr,
+ "UDP OPTIONS\n"
+ " remoteip - Remote ip address\n"
+ " remoteport - Remote port\n"
+ " localip - Local ip address\n"
+ " localport - Local port\n\n");
+ _print_bearer_opts();
+}
+
+static void cmd_bearer_get_l2_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr,
+ "Usage: %s bearer get OPTION media %s device DEVICE\n",
+ cmdl->argv[0], media);
+ _print_bearer_opts();
+}
+
+
+static int bearer_dump_udp_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct sockaddr_storage *addr;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_UDP_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+
+ if (!info[TIPC_NLA_UDP_REMOTE])
+ return MNL_CB_ERROR;
+
+ addr = mnl_attr_get_payload(info[TIPC_NLA_UDP_REMOTE]);
+
+ if (addr->ss_family == AF_INET) {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *) addr;
+
+ printf("%s\n", inet_ntoa(ipv4->sin_addr));
+ } else if (addr->ss_family == AF_INET6) {
+ char straddr[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) addr;
+
+ if (!inet_ntop(AF_INET6, &ipv6->sin6_addr, straddr,
+ sizeof(straddr))) {
+ fprintf(stderr, "error, parsing IPv6 addr\n");
+ return MNL_CB_ERROR;
+ }
+ printf("%s\n", straddr);
+
+ } else {
+ return MNL_CB_ERROR;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int bearer_get_udp_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct cb_data *cb_data = (struct cb_data *) data;
+ struct sockaddr_storage *addr;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1] = {};
+ struct nlattr *opts[TIPC_NLA_UDP_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_BEARER])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_BEARER], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_BEARER_UDP_OPTS], parse_attrs, opts);
+ if (!opts[TIPC_NLA_UDP_LOCAL])
+ return MNL_CB_ERROR;
+
+ if ((cb_data->attr == TIPC_NLA_UDP_REMOTE) &&
+ (cb_data->prop == UDP_PROP_IP) &&
+ opts[TIPC_NLA_UDP_MULTI_REMOTEIP]) {
+ struct mnlu_gen_socket bearer_nlg;
+ struct nlattr *attr;
+ struct nlmsghdr *h;
+ const char *bname;
+ int err = 0;
+
+ err = mnlu_gen_socket_open(&bearer_nlg, TIPC_GENL_V2_NAME,
+ TIPC_GENL_V2_VERSION);
+ if (err)
+ return -1;
+
+ h = mnlu_gen_socket_cmd_prepare(&bearer_nlg,
+ TIPC_NL_UDP_GET_REMOTEIP,
+ NLM_F_REQUEST | NLM_F_DUMP);
+ if (!h) {
+ fprintf(stderr, "error, message initialization failed\n");
+ mnlu_gen_socket_close(&bearer_nlg);
+ return -1;
+ }
+
+ attr = mnl_attr_nest_start(h, TIPC_NLA_BEARER);
+ bname = mnl_attr_get_str(attrs[TIPC_NLA_BEARER_NAME]);
+ mnl_attr_put_strz(h, TIPC_NLA_BEARER_NAME, bname);
+ mnl_attr_nest_end(h, attr);
+
+ err = mnlu_gen_socket_sndrcv(&bearer_nlg, h,
+ bearer_dump_udp_cb, NULL);
+ mnlu_gen_socket_close(&bearer_nlg);
+ return err;
+ }
+
+ addr = mnl_attr_get_payload(opts[cb_data->attr]);
+
+ if (addr->ss_family == AF_INET) {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *) addr;
+
+ switch (cb_data->prop) {
+ case UDP_PROP_IP:
+ printf("%s\n", inet_ntoa(ipv4->sin_addr));
+ break;
+ case UDP_PROP_PORT:
+ printf("%u\n", ntohs(ipv4->sin_port));
+ break;
+ default:
+ return MNL_CB_ERROR;
+ }
+
+ } else if (addr->ss_family == AF_INET6) {
+ char straddr[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) addr;
+
+ switch (cb_data->prop) {
+ case UDP_PROP_IP:
+ if (!inet_ntop(AF_INET6, &ipv6->sin6_addr, straddr,
+ sizeof(straddr))) {
+ fprintf(stderr, "error, parsing IPv6 addr\n");
+ return MNL_CB_ERROR;
+ }
+ printf("%s\n", straddr);
+ break;
+ case UDP_PROP_PORT:
+ printf("%u\n", ntohs(ipv6->sin6_port));
+ break;
+ default:
+ return MNL_CB_ERROR;
+ }
+
+ } else {
+ return MNL_CB_ERROR;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int bearer_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int *prop = data;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1] = {};
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_BEARER])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_BEARER], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_BEARER_PROP])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_BEARER_PROP], parse_attrs, props);
+ if (!props[*prop])
+ return MNL_CB_ERROR;
+
+ printf("%u\n", mnl_attr_get_u32(props[*prop]));
+
+ return MNL_CB_OK;
+}
+
+static int cmd_bearer_get_media(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err;
+ char *media;
+ struct opt *opt;
+ struct cb_data cb_data = {0};
+ struct nlattr *attrs;
+ struct opt opts[] = {
+ { "localip", OPT_KEY, NULL },
+ { "localport", OPT_KEY, NULL },
+ { "remoteip", OPT_KEY, NULL },
+ { "remoteport", OPT_KEY, NULL },
+ { "name", OPT_KEYVAL, NULL },
+ { "media", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct tipc_sup_media sup_media[] = {
+ { "udp", "name", cmd_bearer_get_udp_help},
+ { NULL, },
+ };
+
+ /* Rewind optind to include media in the option list */
+ cmdl->optind--;
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ if (!(opt = get_opt(opts, "media"))) {
+ fprintf(stderr, "error, missing media value\n");
+ return -EINVAL;
+ }
+ media = opt->val;
+
+ if (help_flag) {
+ cmd_bearer_get_udp_help(cmdl, media);
+ return -EINVAL;
+ }
+ if (strcmp(media, "udp") != 0) {
+ fprintf(stderr, "error, no \"%s\" media specific options\n", media);
+ return -EINVAL;
+ }
+ if (!(opt = get_opt(opts, "name"))) {
+ fprintf(stderr, "error, missing media name\n");
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_BEARER_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER);
+ err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media);
+ if (err)
+ return err;
+ mnl_attr_nest_end(nlh, attrs);
+ cb_data.nlh = nlh;
+
+ if (has_opt(opts, "localip")) {
+ cb_data.attr = TIPC_NLA_UDP_LOCAL;
+ cb_data.prop = UDP_PROP_IP;
+ return msg_doit(nlh, bearer_get_udp_cb, &cb_data);
+ } else if (has_opt(opts, "localport")) {
+ cb_data.attr = TIPC_NLA_UDP_LOCAL;
+ cb_data.prop = UDP_PROP_PORT;
+ return msg_doit(nlh, bearer_get_udp_cb, &cb_data);
+ } else if (has_opt(opts, "remoteip")) {
+ cb_data.attr = TIPC_NLA_UDP_REMOTE;
+ cb_data.prop = UDP_PROP_IP;
+ return msg_doit(nlh, bearer_get_udp_cb, &cb_data);
+ } else if (has_opt(opts, "remoteport")) {
+ cb_data.attr = TIPC_NLA_UDP_REMOTE;
+ cb_data.prop = UDP_PROP_PORT;
+ return msg_doit(nlh, bearer_get_udp_cb, &cb_data);
+ }
+ fprintf(stderr, "error, missing UDP option\n");
+ return -EINVAL;
+}
+
+static int cmd_bearer_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err;
+ int prop;
+ struct nlattr *attrs;
+ struct opt opts[] = {
+ { "device", OPT_KEYVAL, NULL },
+ { "media", OPT_KEYVAL, NULL },
+ { "name", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct tipc_sup_media sup_media[] = {
+ { "udp", "name", cmd_bearer_get_udp_help},
+ { "eth", "device", cmd_bearer_get_l2_help },
+ { "ib", "device", cmd_bearer_get_l2_help },
+ { NULL, },
+ };
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (strcmp(cmd->cmd, "priority") == 0)
+ prop = TIPC_NLA_PROP_PRIO;
+ else if ((strcmp(cmd->cmd, "tolerance") == 0))
+ prop = TIPC_NLA_PROP_TOL;
+ else if ((strcmp(cmd->cmd, "window") == 0))
+ prop = TIPC_NLA_PROP_WIN;
+ else if ((strcmp(cmd->cmd, "mtu") == 0))
+ prop = TIPC_NLA_PROP_MTU;
+ else
+ return -EINVAL;
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ if (prop == TIPC_NLA_PROP_MTU) {
+ char *media = cmd_get_media_type(cmd, cmdl, opts);
+
+ if (!media)
+ return -EINVAL;
+ else if (strcmp(media, "udp")) {
+ fprintf(stderr, "error, not supported for media\n");
+ return -EINVAL;
+ }
+ }
+
+ nlh = msg_init(TIPC_NL_BEARER_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER);
+ err = nl_add_bearer_name(nlh, cmd, cmdl, opts, sup_media);
+ if (err)
+ return err;
+ mnl_attr_nest_end(nlh, attrs);
+
+ return msg_doit(nlh, bearer_get_cb, &prop);
+}
+
+static int cmd_bearer_get(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "priority", cmd_bearer_get_prop, cmd_bearer_get_help },
+ { "tolerance", cmd_bearer_get_prop, cmd_bearer_get_help },
+ { "window", cmd_bearer_get_prop, cmd_bearer_get_help },
+ { "mtu", cmd_bearer_get_prop, cmd_bearer_get_help },
+ { "media", cmd_bearer_get_media, cmd_bearer_get_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static int bearer_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_BEARER]) {
+ fprintf(stderr, "No bearer in netlink response\n");
+ return MNL_CB_ERROR;
+ }
+
+ mnl_attr_parse_nested(info[TIPC_NLA_BEARER], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_BEARER_NAME]) {
+ fprintf(stderr, "Bearer name missing in netlink response\n");
+ return MNL_CB_ERROR;
+ }
+
+ printf("%s\n", mnl_attr_get_str(attrs[TIPC_NLA_BEARER_NAME]));
+
+ return MNL_CB_OK;
+}
+
+static int cmd_bearer_list(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ if (help_flag) {
+ fprintf(stderr, "Usage: %s bearer list\n", cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_BEARER_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ return msg_dumpit(nlh, bearer_list_cb, NULL);
+}
+
+void cmd_bearer_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s bearer COMMAND [ARGS] ...\n"
+ "\n"
+ "COMMANDS\n"
+ " add - Add data to existing bearer\n"
+ " enable - Enable a bearer\n"
+ " disable - Disable a bearer\n"
+ " set - Set various bearer properties\n"
+ " get - Get various bearer properties\n"
+ " list - List bearers\n", cmdl->argv[0]);
+}
+
+int cmd_bearer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "add", cmd_bearer_add, cmd_bearer_add_help },
+ { "disable", cmd_bearer_disable, cmd_bearer_disable_help },
+ { "enable", cmd_bearer_enable, cmd_bearer_enable_help },
+ { "get", cmd_bearer_get, cmd_bearer_get_help },
+ { "list", cmd_bearer_list, NULL },
+ { "set", cmd_bearer_set, cmd_bearer_set_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
diff --git a/tipc/bearer.h b/tipc/bearer.h
new file mode 100644
index 0000000..a934465
--- /dev/null
+++ b/tipc/bearer.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * bearer.h TIPC bearer functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_BEARER_H
+#define _TIPC_BEARER_H
+
+#include "cmdl.h"
+
+extern int help_flag;
+
+int cmd_bearer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, void *data);
+void cmd_bearer_help(struct cmdl *cmdl);
+
+void print_bearer_media(void);
+int cmd_get_unique_bearer_name(const struct cmd *cmd, struct cmdl *cmdl,
+ struct opt *opts, char *bname,
+ const struct tipc_sup_media *sup_media);
+#endif
diff --git a/tipc/cmdl.c b/tipc/cmdl.c
new file mode 100644
index 0000000..152ddb5
--- /dev/null
+++ b/tipc/cmdl.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * cmdl.c Framework for handling command line options.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "cmdl.h"
+
+static const struct cmd *find_cmd(const struct cmd *cmds, char *str)
+{
+ const struct cmd *c;
+ const struct cmd *match = NULL;
+
+ for (c = cmds; c->cmd; c++) {
+ if (strstr(c->cmd, str) != c->cmd)
+ continue;
+ if (match)
+ return NULL;
+ match = c;
+ }
+
+ return match;
+}
+
+struct opt *find_opt(struct opt *opts, char *str)
+{
+ struct opt *o;
+ struct opt *match = NULL;
+
+ for (o = opts; o->key; o++) {
+ if (strstr(o->key, str) != o->key)
+ continue;
+ if (match)
+ return NULL;
+
+ match = o;
+ }
+
+ return match;
+}
+
+struct opt *get_opt(struct opt *opts, char *key)
+{
+ struct opt *o;
+
+ for (o = opts; o->key; o++) {
+ if (strcmp(o->key, key) == 0 && o->val)
+ return o;
+ }
+
+ return NULL;
+}
+
+bool has_opt(struct opt *opts, char *key)
+{
+ return get_opt(opts, key) ? true : false;
+}
+
+char *shift_cmdl(struct cmdl *cmdl)
+{
+ int next;
+
+ if (cmdl->optind < cmdl->argc)
+ next = (cmdl->optind)++;
+ else
+ next = cmdl->argc;
+
+ return cmdl->argv[next];
+}
+
+/* Returns the number of options parsed or a negative error code upon failure */
+int parse_opts(struct opt *opts, struct cmdl *cmdl)
+{
+ int i;
+ int cnt = 0;
+
+ for (i = cmdl->optind; i < cmdl->argc; i++) {
+ struct opt *o;
+
+ o = find_opt(opts, cmdl->argv[i]);
+ if (!o) {
+ fprintf(stderr, "error, invalid option \"%s\"\n",
+ cmdl->argv[i]);
+ return -EINVAL;
+ }
+ if (o->flag & OPT_KEYVAL) {
+ cmdl->optind++;
+ i++;
+ }
+ cnt++;
+ o->val = cmdl->argv[i];
+ cmdl->optind++;
+ }
+
+ return cnt;
+}
+
+int run_cmd(struct nlmsghdr *nlh, const struct cmd *caller,
+ const struct cmd *cmds, struct cmdl *cmdl, void *data)
+{
+ char *name;
+ const struct cmd *cmd;
+
+ if ((cmdl->optind) >= cmdl->argc) {
+ if (caller->help)
+ (caller->help)(cmdl);
+ return -EINVAL;
+ }
+ name = cmdl->argv[cmdl->optind];
+ (cmdl->optind)++;
+
+ cmd = find_cmd(cmds, name);
+ if (!cmd) {
+ /* Show help about last command if we don't find this one */
+ if (help_flag && caller->help) {
+ (caller->help)(cmdl);
+ } else {
+ fprintf(stderr, "error, invalid command \"%s\"\n", name);
+ fprintf(stderr, "use --help for command help\n");
+ }
+ return -EINVAL;
+ }
+
+ return (cmd->func)(nlh, cmd, cmdl, data);
+}
diff --git a/tipc/cmdl.h b/tipc/cmdl.h
new file mode 100644
index 0000000..18fe51b
--- /dev/null
+++ b/tipc/cmdl.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * cmdl.h Framework for handling command line options.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_CMDL_H
+#define _TIPC_CMDL_H
+
+#include <libmnl/libmnl.h>
+
+extern int help_flag;
+
+enum {
+ OPT_KEY = (1 << 0),
+ OPT_KEYVAL = (1 << 1),
+};
+
+struct cmdl {
+ int optind;
+ int argc;
+ char **argv;
+};
+
+struct tipc_sup_media {
+ char *media;
+ char *identifier;
+ void (*help)(struct cmdl *cmdl, char *media);
+};
+
+struct cmd {
+ const char *cmd;
+ int (*func)(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data);
+ void (*help)(struct cmdl *cmdl);
+};
+
+struct opt {
+ const char *key;
+ uint16_t flag;
+ char *val;
+};
+
+struct opt *find_opt(struct opt *opts, char *str);
+struct opt *get_opt(struct opt *opts, char *key);
+bool has_opt(struct opt *opts, char *key);
+int parse_opts(struct opt *opts, struct cmdl *cmdl);
+char *shift_cmdl(struct cmdl *cmdl);
+
+int run_cmd(struct nlmsghdr *nlh, const struct cmd *caller,
+ const struct cmd *cmds, struct cmdl *cmdl, void *data);
+
+#endif
diff --git a/tipc/link.c b/tipc/link.c
new file mode 100644
index 0000000..f91c300
--- /dev/null
+++ b/tipc/link.c
@@ -0,0 +1,1248 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * link.c TIPC link functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/tipc_netlink.h>
+#include <linux/tipc.h>
+#include <linux/genetlink.h>
+#include <libmnl/libmnl.h>
+
+#include "mnl_utils.h"
+#include "cmdl.h"
+#include "msg.h"
+#include "link.h"
+#include "bearer.h"
+#include "utils.h"
+
+#define PRIORITY_STR "priority"
+#define TOLERANCE_STR "tolerance"
+#define WINDOW_STR "window"
+#define BROADCAST_STR "broadcast"
+
+static const char tipc_bclink_name[] = "broadcast-link";
+
+static int link_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_LINK])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_LINK_NAME])
+ return MNL_CB_ERROR;
+
+ print_string(PRINT_FP, NULL, "%s: ",
+ mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]));
+ if (attrs[TIPC_NLA_LINK_UP])
+ print_string(PRINT_ANY,
+ mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]),"%s\n", "up");
+ else
+ print_string(PRINT_ANY,
+ mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]), "%s\n", "down");
+ return MNL_CB_OK;
+}
+
+static int cmd_link_list(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err = 0;
+
+ if (help_flag) {
+ fprintf(stderr, "Usage: %s link list\n", cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_LINK_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ new_json_obj(json);
+ open_json_object(NULL);
+ err = msg_dumpit(nlh, link_list_cb, NULL);
+ close_json_object();
+ delete_json_obj();
+ return err;
+}
+
+static int link_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int *prop = data;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {};
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_LINK])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_LINK_PROP])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, props);
+ if (!props[*prop])
+ return MNL_CB_ERROR;
+
+ new_json_obj(json);
+ open_json_object(NULL);
+ switch (*prop) {
+ case TIPC_NLA_PROP_PRIO:
+ print_uint(PRINT_ANY, PRIORITY_STR, "%u\n", mnl_attr_get_u32(props[*prop]));
+ break;
+ case TIPC_NLA_PROP_TOL:
+ print_uint(PRINT_ANY, TOLERANCE_STR, "%u\n", mnl_attr_get_u32(props[*prop]));
+ break;
+ case TIPC_NLA_PROP_WIN:
+ print_uint(PRINT_ANY, WINDOW_STR, "%u\n", mnl_attr_get_u32(props[*prop]));
+ break;
+ default:
+ break;
+ }
+ close_json_object();
+ delete_json_obj();
+ return MNL_CB_OK;
+}
+
+static int cmd_link_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int prop;
+ struct nlattr *attrs;
+ struct opt *opt;
+ struct opt opts[] = {
+ { "link", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+
+ if (strcmp(cmd->cmd, PRIORITY_STR) == 0)
+ prop = TIPC_NLA_PROP_PRIO;
+ else if ((strcmp(cmd->cmd, TOLERANCE_STR) == 0))
+ prop = TIPC_NLA_PROP_TOL;
+ else if ((strcmp(cmd->cmd, WINDOW_STR) == 0))
+ prop = TIPC_NLA_PROP_WIN;
+ else
+ return -EINVAL;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ nlh = msg_init(TIPC_NL_LINK_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ opt = get_opt(opts, "link");
+ if (!opt) {
+ fprintf(stderr, "error, missing link\n");
+ return -EINVAL;
+ }
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+ mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, opt->val);
+ mnl_attr_nest_end(nlh, attrs);
+
+ return msg_doit(nlh, link_get_cb, &prop);
+}
+
+static void cmd_link_get_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s link get PPROPERTY link LINK\n\n"
+ "PROPERTIES\n"
+ " tolerance - Get link tolerance\n"
+ " priority - Get link priority\n"
+ " window - Get link window\n"
+ " broadcast - Get link broadcast\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_get_bcast_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int *prop = data;
+ int prop_ratio = TIPC_NLA_PROP_BROADCAST_RATIO;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {};
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {};
+ int bc_mode;
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_LINK])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_LINK_PROP])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, props);
+ if (!props[*prop])
+ return MNL_CB_ERROR;
+
+ bc_mode = mnl_attr_get_u32(props[*prop]);
+
+ new_json_obj(json);
+ open_json_object(NULL);
+ switch (bc_mode) {
+ case 0x1:
+ print_string(PRINT_ANY, "method", "%s\n", "BROADCAST");
+ break;
+ case 0x2:
+ print_string(PRINT_ANY, "method", "%s\n", "REPLICAST");
+ break;
+ case 0x4:
+ print_string(PRINT_ANY, "method", "%s", "AUTOSELECT");
+ close_json_object();
+ open_json_object(NULL);
+ print_uint(PRINT_ANY, "ratio", " ratio:%u\n",
+ mnl_attr_get_u32(props[prop_ratio]));
+ break;
+ default:
+ print_string(PRINT_ANY, NULL, "UNKNOWN\n", NULL);
+ break;
+ }
+ close_json_object();
+ delete_json_obj();
+ return MNL_CB_OK;
+}
+
+static void cmd_link_get_bcast_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s link get PPROPERTY\n\n"
+ "PROPERTIES\n"
+ " broadcast - Get link broadcast\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_get_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int prop = TIPC_NLA_PROP_BROADCAST;
+ struct nlattr *attrs;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_LINK_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+ /* Direct to broadcast-link setting */
+ mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, tipc_bclink_name);
+ mnl_attr_nest_end(nlh, attrs);
+ return msg_doit(nlh, cmd_link_get_bcast_cb, &prop);
+}
+
+static int cmd_link_get(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { PRIORITY_STR, cmd_link_get_prop, cmd_link_get_help },
+ { TOLERANCE_STR, cmd_link_get_prop, cmd_link_get_help },
+ { WINDOW_STR, cmd_link_get_prop, cmd_link_get_help },
+ { BROADCAST_STR, cmd_link_get_bcast, cmd_link_get_bcast_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_link_stat_reset_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s link stat reset link LINK\n\n", cmdl->argv[0]);
+}
+
+static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ char *link;
+ struct opt *opt;
+ struct nlattr *nest;
+ struct opt opts[] = {
+ { "link", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (parse_opts(opts, cmdl) != 1) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_LINK_RESET_STATS);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ opt = get_opt(opts, "link");
+ if (!opt) {
+ fprintf(stderr, "error, missing link\n");
+ return -EINVAL;
+ }
+ link = opt->val;
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+ mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, link);
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static uint32_t perc(uint32_t count, uint32_t total)
+{
+ return (count * 100 + (total / 2)) / total;
+}
+
+static int _show_link_stat(const char *name, struct nlattr *attrs[],
+ struct nlattr *prop[], struct nlattr *stats[])
+{
+ uint32_t proft;
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "link", "Link <%s>\n", name);
+ print_string(PRINT_JSON, "state", NULL, NULL);
+ open_json_array(PRINT_JSON, NULL);
+ if (attrs[TIPC_NLA_LINK_ACTIVE])
+ print_string(PRINT_ANY, NULL, " %s", "ACTIVE");
+ else if (attrs[TIPC_NLA_LINK_UP])
+ print_string(PRINT_ANY, NULL, " %s", "STANDBY");
+ else
+ print_string(PRINT_ANY, NULL, " %s", "DEFUNCT");
+ close_json_array(PRINT_JSON, NULL);
+
+ print_uint(PRINT_ANY, "mtu", " MTU:%u",
+ mnl_attr_get_u32(attrs[TIPC_NLA_LINK_MTU]));
+ print_uint(PRINT_ANY, PRIORITY_STR, " Priority:%u",
+ mnl_attr_get_u32(prop[TIPC_NLA_PROP_PRIO]));
+ print_uint(PRINT_ANY, TOLERANCE_STR, " Tolerance:%u ms",
+ mnl_attr_get_u32(prop[TIPC_NLA_PROP_TOL]));
+ print_uint(PRINT_ANY, WINDOW_STR, " Window:%u packets\n",
+ mnl_attr_get_u32(prop[TIPC_NLA_PROP_WIN]));
+
+ open_json_object("rx packets");
+ print_uint(PRINT_ANY, "rx packets", " RX packets:%u",
+ mnl_attr_get_u32(attrs[TIPC_NLA_LINK_RX]) -
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_INFO]));
+ print_uint(PRINT_ANY, "fragments", " fragments:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]));
+ print_uint(PRINT_ANY, "fragmented", "/%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]));
+ print_uint(PRINT_ANY, "bundles", " bundles:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]));
+ print_uint(PRINT_ANY, "bundled", "/%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
+ close_json_object();
+
+ open_json_object("tx packets");
+ print_uint(PRINT_ANY, "tx packets", " TX packets:%u",
+ mnl_attr_get_u32(attrs[TIPC_NLA_LINK_TX]) -
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_INFO]));
+ print_uint(PRINT_ANY, "fragments", " fragments:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]));
+ print_uint(PRINT_ANY, "fragmented", "/%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]));
+ print_uint(PRINT_ANY, "bundles", " bundles:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]));
+ print_uint(PRINT_ANY, "bundled", "/%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
+ close_json_object();
+
+ proft = mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]);
+ print_uint(PRINT_ANY, "tx profile sample", " TX profile sample:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]));
+ print_uint(PRINT_ANY, "packets average", " packets average:%u octets\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) / proft);
+
+ print_uint(PRINT_ANY, "0-64", " 0-64:%u%%",
+ perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]), proft));
+ print_uint(PRINT_ANY, "-256", " -256:%u%%",
+ perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]), proft));
+ print_uint(PRINT_ANY, "-1024", " -1024:%u%%",
+ perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]), proft));
+ print_uint(PRINT_ANY, "-4096", " -4096:%u%%",
+ perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P3]), proft));
+ print_uint(PRINT_ANY, "-16384", " -16384:%u%%",
+ perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P4]), proft));
+ print_uint(PRINT_ANY, "-32768", " -32768:%u%%",
+ perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P5]), proft));
+ print_uint(PRINT_ANY, "-66000", " -66000:%u%%\n",
+ perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P6]), proft));
+
+ open_json_object("rx states");
+ print_uint(PRINT_ANY, "rx states", " RX states:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_STATES]));
+ print_uint(PRINT_ANY, "probes", " probes:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_PROBES]));
+ print_uint(PRINT_ANY, "naks", " naks:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]));
+ print_uint(PRINT_ANY, "defs", " defs:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]));
+ print_uint(PRINT_ANY, "dups", " dups:%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
+ close_json_object();
+
+ open_json_object("tx states");
+ print_uint(PRINT_ANY, "tx states", " TX states:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_STATES]));
+ print_uint(PRINT_ANY, "probes", " probes:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_PROBES]));
+ print_uint(PRINT_ANY, "naks", " naks:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]));
+ print_uint(PRINT_ANY, "acks", " acks:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]));
+ print_uint(PRINT_ANY, "retrans", " retrans:%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
+ close_json_object();
+
+ print_uint(PRINT_ANY, "congestion link", " Congestion link:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
+ print_uint(PRINT_ANY, "send queue max", " Send queue max:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
+ print_uint(PRINT_ANY, "avg", " avg:%u\n\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
+
+ close_json_object();
+ return MNL_CB_OK;
+}
+
+static int _show_bc_link_stat(const char *name, struct nlattr *prop[],
+ struct nlattr *stats[])
+{
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "link", "Link <%s>\n", name);
+ print_uint(PRINT_ANY, WINDOW_STR, " Window:%u packets\n",
+ mnl_attr_get_u32(prop[TIPC_NLA_PROP_WIN]));
+
+ open_json_object("rx packets");
+ print_uint(PRINT_ANY, "rx packets", " RX packets:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_INFO]));
+ print_uint(PRINT_ANY, "fragments", " fragments:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]));
+ print_uint(PRINT_ANY, "fragmented", "/%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]));
+ print_uint(PRINT_ANY, "bundles", " bundles:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]));
+ print_uint(PRINT_ANY, "bundled", "/%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
+ close_json_object();
+
+ open_json_object("tx packets");
+ print_uint(PRINT_ANY, "tx packets", " TX packets:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_INFO]));
+ print_uint(PRINT_ANY, "fragments", " fragments:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]));
+ print_uint(PRINT_ANY, "fragmented", "/%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]));
+ print_uint(PRINT_ANY, "bundles", " bundles:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]));
+ print_uint(PRINT_ANY, "bundled", "/%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
+ close_json_object();
+
+ open_json_object("rx naks");
+ print_uint(PRINT_ANY, "rx naks", " RX naks:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]));
+ print_uint(PRINT_ANY, "defs", " defs:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]));
+ print_uint(PRINT_ANY, "dups", " dups:%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
+ close_json_object();
+
+ open_json_object("tx naks");
+ print_uint(PRINT_ANY, "tx naks", " TX naks:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]));
+ print_uint(PRINT_ANY, "acks", " acks:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]));
+ print_uint(PRINT_ANY, "retrans", " retrans:%u\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
+ close_json_object();
+
+ print_uint(PRINT_ANY, "congestion link", " Congestion link:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
+ print_uint(PRINT_ANY, "send queue max", " Send queue max:%u",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
+ print_uint(PRINT_ANY, "avg", " avg:%u\n\n",
+ mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+static int link_stat_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+ const char *name;
+ const char *link = data;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {};
+ struct nlattr *prop[TIPC_NLA_PROP_MAX + 1] = {};
+ struct nlattr *stats[TIPC_NLA_STATS_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_LINK])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_LINK_NAME] || !attrs[TIPC_NLA_LINK_PROP] ||
+ !attrs[TIPC_NLA_LINK_STATS])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, prop);
+ mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_STATS], parse_attrs, stats);
+
+ name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]);
+
+ /* If a link is passed, skip all but that link.
+ * Support a substring matching as well.
+ */
+ if (link && !strstr(name, link))
+ return MNL_CB_OK;
+
+ if (attrs[TIPC_NLA_LINK_BROADCAST]) {
+ return _show_bc_link_stat(name, prop, stats);
+ }
+
+ return _show_link_stat(name, attrs, prop, stats);
+}
+
+static void cmd_link_stat_show_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s link stat show [ link { LINK | SUBSTRING | all } ]\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ char *link = NULL;
+ struct opt *opt;
+ struct opt opts[] = {
+ { "link", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct nlattr *attrs;
+ int err = 0;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_LINK_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ opt = get_opt(opts, "link");
+ if (opt) {
+ if (strcmp(opt->val, "all"))
+ link = opt->val;
+ /* Set the flag to dump all bc links */
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+ mnl_attr_put(nlh, TIPC_NLA_LINK_BROADCAST, 0, NULL);
+ mnl_attr_nest_end(nlh, attrs);
+ }
+
+ new_json_obj(json);
+ err = msg_dumpit(nlh, link_stat_show_cb, link);
+ delete_json_obj();
+ return err;
+}
+
+static void cmd_link_stat_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s link stat COMMAND [ARGS]\n\n"
+ "COMMANDS:\n"
+ " reset - Reset link statistics for link\n"
+ " show - Get link priority\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_stat(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "reset", cmd_link_stat_reset, cmd_link_stat_reset_help },
+ { "show", cmd_link_stat_show, cmd_link_stat_show_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_link_set_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s link set PPROPERTY link LINK\n\n"
+ "PROPERTIES\n"
+ " tolerance TOLERANCE - Set link tolerance\n"
+ " priority PRIORITY - Set link priority\n"
+ " window WINDOW - Set link window\n"
+ " broadcast BROADCAST - Set link broadcast\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int val;
+ int prop;
+ struct nlattr *props;
+ struct nlattr *attrs;
+ struct opt *opt;
+ struct opt opts[] = {
+ { "link", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+
+ if (strcmp(cmd->cmd, PRIORITY_STR) == 0)
+ prop = TIPC_NLA_PROP_PRIO;
+ else if ((strcmp(cmd->cmd, TOLERANCE_STR) == 0))
+ prop = TIPC_NLA_PROP_TOL;
+ else if ((strcmp(cmd->cmd, WINDOW_STR) == 0))
+ prop = TIPC_NLA_PROP_WIN;
+ else
+ return -EINVAL;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (cmdl->optind >= cmdl->argc) {
+ fprintf(stderr, "error, missing value\n");
+ return -EINVAL;
+ }
+ val = atoi(shift_cmdl(cmdl));
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ nlh = msg_init(TIPC_NL_LINK_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+
+ opt = get_opt(opts, "link");
+ if (!opt) {
+ fprintf(stderr, "error, missing link\n");
+ return -EINVAL;
+ }
+ mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, opt->val);
+
+ props = mnl_attr_nest_start(nlh, TIPC_NLA_LINK_PROP);
+ mnl_attr_put_u32(nlh, prop, val);
+ mnl_attr_nest_end(nlh, props);
+
+ mnl_attr_nest_end(nlh, attrs);
+
+ return msg_doit(nlh, link_get_cb, &prop);
+}
+
+static void cmd_link_set_bcast_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s link set broadcast PROPERTY\n\n"
+ "PROPERTIES\n"
+ " BROADCAST - Forces all multicast traffic to be\n"
+ " transmitted via broadcast only,\n"
+ " irrespective of cluster size and number\n"
+ " of destinations\n\n"
+ " REPLICAST - Forces all multicast traffic to be\n"
+ " transmitted via replicast only,\n"
+ " irrespective of cluster size and number\n"
+ " of destinations\n\n"
+ " AUTOSELECT - Auto switching to broadcast or replicast\n"
+ " depending on cluster size and destination\n"
+ " node number\n\n"
+ " ratio SIZE - Set the AUTOSELECT criteria, percentage of\n"
+ " destination nodes vs cluster size\n\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_set_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ struct nlattr *props;
+ struct nlattr *attrs;
+ struct opt *opt;
+ struct opt opts[] = {
+ { "BROADCAST", OPT_KEY, NULL },
+ { "REPLICAST", OPT_KEY, NULL },
+ { "AUTOSELECT", OPT_KEY, NULL },
+ { "ratio", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ int method = 0;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ for (opt = opts; opt->key; opt++)
+ if (opt->val)
+ break;
+
+ if (!opt || !opt->key) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_LINK_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+ /* Direct to broadcast-link setting */
+ mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, tipc_bclink_name);
+ props = mnl_attr_nest_start(nlh, TIPC_NLA_LINK_PROP);
+
+ if (get_opt(opts, "BROADCAST"))
+ method = 0x1;
+ else if (get_opt(opts, "REPLICAST"))
+ method = 0x2;
+ else if (get_opt(opts, "AUTOSELECT"))
+ method = 0x4;
+
+ opt = get_opt(opts, "ratio");
+ if (!method && !opt) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (method)
+ mnl_attr_put_u32(nlh, TIPC_NLA_PROP_BROADCAST, method);
+
+ if (opt)
+ mnl_attr_put_u32(nlh, TIPC_NLA_PROP_BROADCAST_RATIO,
+ atoi(opt->val));
+
+ mnl_attr_nest_end(nlh, props);
+ mnl_attr_nest_end(nlh, attrs);
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int cmd_link_set(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { PRIORITY_STR, cmd_link_set_prop, cmd_link_set_help },
+ { TOLERANCE_STR, cmd_link_set_prop, cmd_link_set_help },
+ { WINDOW_STR, cmd_link_set_prop, cmd_link_set_help },
+ { BROADCAST_STR, cmd_link_set_bcast, cmd_link_set_bcast_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static int cmd_link_mon_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int size;
+ struct nlattr *attrs;
+
+ if (cmdl->argc != cmdl->optind + 1) {
+ fprintf(stderr, "error, missing value\n");
+ return -EINVAL;
+ }
+ size = atoi(shift_cmdl(cmdl));
+
+ nlh = msg_init(TIPC_NL_MON_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_MON);
+
+ mnl_attr_put_u32(nlh, TIPC_NLA_MON_ACTIVATION_THRESHOLD, size);
+
+ mnl_attr_nest_end(nlh, attrs);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int link_mon_summary_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_MON_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_MON])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_MON], parse_attrs, attrs);
+
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "bearer", "\nbearer %s\n",
+ mnl_attr_get_str(attrs[TIPC_NLA_MON_BEARER_NAME]));
+
+ print_uint(PRINT_ANY, "table_generation", " table_generation %u\n",
+ mnl_attr_get_u32(attrs[TIPC_NLA_MON_LISTGEN]));
+ print_uint(PRINT_ANY, "cluster_size", " cluster_size %u\n",
+ mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEERCNT]));
+ print_string(PRINT_ANY, "algorithm", " algorithm %s\n",
+ attrs[TIPC_NLA_MON_ACTIVE] ? "overlapping-ring" : "full-mesh");
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+static int cmd_link_mon_summary(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int err = 0;
+
+ if (help_flag) {
+ fprintf(stderr, "Usage: %s monitor summary\n", cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_MON_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ new_json_obj(json);
+ err = msg_dumpit(nlh, link_mon_summary_cb, NULL);
+ delete_json_obj();
+
+ return err;
+}
+
+#define STATUS_WIDTH 7
+#define MAX_NODE_WIDTH 14 /* 255.4095.4095 */
+#define MAX_DOM_GEN_WIDTH 11 /* 65535 */
+#define DIRECTLY_MON_WIDTH 10
+
+#define APPL_NODE_STATUS_WIDTH 5
+
+static int map_get(uint64_t up_map, int i)
+{
+ return (up_map & (1 << i)) >> i;
+}
+
+/* print the applied members, since we know the the members
+ * are listed in ascending order, we print only the state
+ */
+static void link_mon_print_applied(uint16_t applied, uint64_t up_map)
+{
+ int i;
+
+ open_json_array(PRINT_JSON, "applied_node_status");
+ for (i = 0; i < applied; i++) {
+ char state_str[2] = {0};
+
+ /* print the delimiter for every -n- entry */
+ if (i && !(i % APPL_NODE_STATUS_WIDTH))
+ print_string(PRINT_FP, NULL, "%s", ",");
+
+ sprintf(state_str, "%c", map_get(up_map, i) ? 'U' : 'D');
+ print_string(PRINT_ANY, NULL, "%s", state_str);
+ }
+ close_json_array(PRINT_JSON, "applied_node_status");
+}
+
+/* print the non applied members, since we don't know
+ * the members, we print them along with the state
+ */
+static void link_mon_print_non_applied(uint16_t applied, uint16_t member_cnt,
+ uint64_t up_map, uint32_t *members)
+{
+ int i;
+ char state;
+
+ open_json_array(PRINT_JSON, "[non_applied_node:status]");
+ print_string(PRINT_FP, NULL, " %s", "[");
+ for (i = applied; i < member_cnt; i++) {
+ char addr_str[16];
+ char full_state[17] = {0};
+
+ /* print the delimiter for every entry */
+ if (i != applied)
+ print_string(PRINT_FP, NULL, "%s", ",");
+
+ sprintf(addr_str, "%x:", members[i]);
+ state = map_get(up_map, i) ? 'U' : 'D';
+ sprintf(full_state, "%s%c", addr_str, state);
+ print_string(PRINT_ANY, NULL, "%s", full_state);
+ }
+ print_string(PRINT_FP, NULL, "%s", "]");
+ close_json_array(PRINT_JSON, "[non_applied_node:status]");
+}
+
+static void link_mon_print_peer_state(const uint32_t addr, const char *status,
+ const char *monitored,
+ const uint32_t dom_gen)
+{
+ char addr_str[16];
+
+ sprintf(addr_str, "%u.%u.%u", tipc_zone(addr), tipc_cluster(addr),
+ tipc_node(addr));
+ if (is_json_context()) {
+ print_string(PRINT_JSON, "node", NULL, addr_str);
+ print_string(PRINT_JSON, "status", NULL, status);
+ print_string(PRINT_JSON, "monitored", NULL, monitored);
+ print_uint(PRINT_JSON, "generation", NULL, dom_gen);
+ } else {
+ printf("%-*s", MAX_NODE_WIDTH, addr_str);
+ printf("%-*s", STATUS_WIDTH, status);
+ printf("%-*s", DIRECTLY_MON_WIDTH, monitored);
+ printf("%-*u", MAX_DOM_GEN_WIDTH, dom_gen);
+ }
+}
+
+static int link_mon_peer_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *attrs[TIPC_NLA_MON_PEER_MAX + 1] = {};
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ uint16_t member_cnt;
+ uint32_t applied;
+ uint32_t dom_gen;
+ uint64_t up_map;
+ char status[16];
+ char monitored[16];
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_MON_PEER])
+ return MNL_CB_ERROR;
+
+ open_json_object(NULL);
+ mnl_attr_parse_nested(info[TIPC_NLA_MON_PEER], parse_attrs, attrs);
+
+ (attrs[TIPC_NLA_MON_PEER_LOCAL] || attrs[TIPC_NLA_MON_PEER_HEAD]) ?
+ strcpy(monitored, "direct") :
+ strcpy(monitored, "indirect");
+
+ attrs[TIPC_NLA_MON_PEER_UP] ?
+ strcpy(status, "up") :
+ strcpy(status, "down");
+
+ dom_gen = attrs[TIPC_NLA_MON_PEER_DOMGEN] ?
+ mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEER_DOMGEN]) : 0;
+
+ link_mon_print_peer_state(mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEER_ADDR]),
+ status, monitored, dom_gen);
+
+ applied = mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEER_APPLIED]);
+
+ if (!applied)
+ goto exit;
+
+ up_map = mnl_attr_get_u64(attrs[TIPC_NLA_MON_PEER_UPMAP]);
+
+ member_cnt = mnl_attr_get_payload_len(attrs[TIPC_NLA_MON_PEER_MEMBERS]);
+
+ /* each tipc address occupies 4 bytes of payload, hence compensate it */
+ member_cnt /= sizeof(uint32_t);
+
+ link_mon_print_applied(applied, up_map);
+
+ link_mon_print_non_applied(applied, member_cnt, up_map,
+ mnl_attr_get_payload(attrs[TIPC_NLA_MON_PEER_MEMBERS]));
+
+exit:
+ print_string(PRINT_FP, NULL, "\n", "");
+
+ close_json_object();
+ return MNL_CB_OK;
+}
+
+static int link_mon_peer_list(uint32_t mon_ref)
+{
+ struct mnlu_gen_socket link_nlg;
+ struct nlmsghdr *nlh;
+ struct nlattr *nest;
+ int err = 0;
+
+ err = mnlu_gen_socket_open(&link_nlg, TIPC_GENL_V2_NAME,
+ TIPC_GENL_V2_VERSION);
+ if (err)
+ return -1;
+ nlh = mnlu_gen_socket_cmd_prepare(&link_nlg, TIPC_NL_MON_PEER_GET,
+ NLM_F_REQUEST | NLM_F_DUMP);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ mnlu_gen_socket_close(&link_nlg);
+ return -1;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_MON);
+ mnl_attr_put_u32(nlh, TIPC_NLA_MON_REF, mon_ref);
+ mnl_attr_nest_end(nlh, nest);
+
+ err = mnlu_gen_socket_sndrcv(&link_nlg, nlh, link_mon_peer_list_cb,
+ NULL);
+ mnlu_gen_socket_close(&link_nlg);
+ return err;
+}
+
+static int link_mon_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_MON_MAX + 1] = {};
+ char *req_bearer = data;
+ const char *bname;
+ const char title[] =
+ "node status monitored generation applied_node_status [non_applied_node:status]";
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_MON])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_MON], parse_attrs, attrs);
+
+ bname = mnl_attr_get_str(attrs[TIPC_NLA_MON_BEARER_NAME]);
+
+ if (*req_bearer && (strcmp(req_bearer, bname) != 0))
+ return MNL_CB_OK;
+
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "bearer", "\nbearer %s\n", bname);
+ print_string(PRINT_FP, NULL, "%s\n", title);
+
+ open_json_array(PRINT_JSON, bname);
+ if (mnl_attr_get_u32(attrs[TIPC_NLA_MON_PEERCNT]))
+ link_mon_peer_list(mnl_attr_get_u32(attrs[TIPC_NLA_MON_REF]));
+ close_json_array(PRINT_JSON, bname);
+
+ close_json_object();
+ return MNL_CB_OK;
+}
+
+static void cmd_link_mon_list_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s monitor list [ media MEDIA ARGS...]\n\n",
+ cmdl->argv[0]);
+ print_bearer_media();
+}
+
+static void cmd_link_mon_list_l2_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr,
+ "Usage: %s monitor list media %s device DEVICE [OPTIONS]\n",
+ cmdl->argv[0], media);
+}
+
+static void cmd_link_mon_list_udp_help(struct cmdl *cmdl, char *media)
+{
+ fprintf(stderr,
+ "Usage: %s monitor list media udp name NAME\n\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_mon_list(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ char bname[TIPC_MAX_BEARER_NAME] = {0};
+ struct opt opts[] = {
+ { "media", OPT_KEYVAL, NULL },
+ { "device", OPT_KEYVAL, NULL },
+ { "name", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct tipc_sup_media sup_media[] = {
+ { "udp", "name", cmd_link_mon_list_udp_help},
+ { "eth", "device", cmd_link_mon_list_l2_help },
+ { "ib", "device", cmd_link_mon_list_l2_help },
+ { NULL, },
+ };
+
+ int err;
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ if (get_opt(opts, "media")) {
+ err = cmd_get_unique_bearer_name(cmd, cmdl, opts, bname,
+ sup_media);
+ if (err)
+ return err;
+ }
+
+ if (help_flag) {
+ cmd->help(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_MON_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ new_json_obj(json);
+ err = msg_dumpit(nlh, link_mon_list_cb, bname);
+ delete_json_obj();
+ return err;
+}
+
+static void cmd_link_mon_set_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s monitor set PPROPERTY\n\n"
+ "PROPERTIES\n"
+ " threshold SIZE - Set monitor activation threshold\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_mon_set(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "threshold", cmd_link_mon_set_prop, NULL },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_link_mon_get_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s monitor get PPROPERTY\n\n"
+ "PROPERTIES\n"
+ " threshold - Get monitor activation threshold\n",
+ cmdl->argv[0]);
+}
+
+static int link_mon_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_MON_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_MON])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_MON], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_MON_ACTIVATION_THRESHOLD])
+ return MNL_CB_ERROR;
+
+ new_json_obj(json);
+ print_uint(PRINT_ANY, "threshold", "%u\n",
+ mnl_attr_get_u32(attrs[TIPC_NLA_MON_ACTIVATION_THRESHOLD]));
+ delete_json_obj();
+
+ return MNL_CB_OK;
+}
+
+static int cmd_link_mon_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+
+ nlh = msg_init(TIPC_NL_MON_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ return msg_doit(nlh, link_mon_get_cb, NULL);
+}
+
+static int cmd_link_mon_get(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "threshold", cmd_link_mon_get_prop, NULL},
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_link_mon_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s montior COMMAND [ARGS] ...\n\n"
+ "COMMANDS\n"
+ " set - Set monitor properties\n"
+ " get - Get monitor properties\n"
+ " list - List all cluster members\n"
+ " summary - Show local node monitor summary\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_link_mon(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "set", cmd_link_mon_set, cmd_link_mon_set_help },
+ { "get", cmd_link_mon_get, cmd_link_mon_get_help },
+ { "list", cmd_link_mon_list, cmd_link_mon_list_help },
+ { "summary", cmd_link_mon_summary, NULL },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+void cmd_link_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s link COMMAND [ARGS] ...\n"
+ "\n"
+ "COMMANDS\n"
+ " list - List links\n"
+ " get - Get various link properties\n"
+ " set - Set various link properties\n"
+ " statistics - Show or reset statistics\n"
+ " monitor - Show or set link supervision\n",
+ cmdl->argv[0]);
+}
+
+int cmd_link(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "get", cmd_link_get, cmd_link_get_help },
+ { "list", cmd_link_list, NULL },
+ { "set", cmd_link_set, cmd_link_set_help },
+ { "statistics", cmd_link_stat, cmd_link_stat_help },
+ { "monitor", cmd_link_mon, cmd_link_mon_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
diff --git a/tipc/link.h b/tipc/link.h
new file mode 100644
index 0000000..a0d4603
--- /dev/null
+++ b/tipc/link.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * link.c TIPC link functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_LINK_H
+#define _TIPC_LINK_H
+
+extern int help_flag;
+
+int cmd_link(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data);
+void cmd_link_help(struct cmdl *cmdl);
+
+#endif
diff --git a/tipc/media.c b/tipc/media.c
new file mode 100644
index 0000000..5ff0c8c
--- /dev/null
+++ b/tipc/media.c
@@ -0,0 +1,273 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * media.c TIPC link functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/tipc_netlink.h>
+#include <linux/genetlink.h>
+
+#include "cmdl.h"
+#include "msg.h"
+#include "media.h"
+
+static int media_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_MEDIA])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_MEDIA], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_MEDIA_NAME])
+ return MNL_CB_ERROR;
+
+ printf("%s\n", mnl_attr_get_str(attrs[TIPC_NLA_MEDIA_NAME]));
+
+ return MNL_CB_OK;
+}
+
+static int cmd_media_list(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ if (help_flag) {
+ fprintf(stderr, "Usage: %s media list\n", cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_MEDIA_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ return msg_dumpit(nlh, media_list_cb, NULL);
+}
+
+static int media_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int *prop = data;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1] = {};
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_MEDIA])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_MEDIA], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_MEDIA_PROP])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_MEDIA_PROP], parse_attrs, props);
+ if (!props[*prop])
+ return MNL_CB_ERROR;
+
+ printf("%u\n", mnl_attr_get_u32(props[*prop]));
+
+ return MNL_CB_OK;
+}
+
+static int cmd_media_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int prop;
+ struct nlattr *nest;
+ struct opt *opt;
+ struct opt opts[] = {
+ { "media", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+
+ if (strcmp(cmd->cmd, "priority") == 0)
+ prop = TIPC_NLA_PROP_PRIO;
+ else if ((strcmp(cmd->cmd, "tolerance") == 0))
+ prop = TIPC_NLA_PROP_TOL;
+ else if ((strcmp(cmd->cmd, "window") == 0))
+ prop = TIPC_NLA_PROP_WIN;
+ else if ((strcmp(cmd->cmd, "mtu") == 0))
+ prop = TIPC_NLA_PROP_MTU;
+ else
+ return -EINVAL;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ nlh = msg_init(TIPC_NL_MEDIA_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ if (!(opt = get_opt(opts, "media"))) {
+ fprintf(stderr, "error, missing media\n");
+ return -EINVAL;
+ }
+
+ if ((prop == TIPC_NLA_PROP_MTU) &&
+ (strcmp(opt->val, "udp"))) {
+ fprintf(stderr, "error, not supported for media\n");
+ return -EINVAL;
+ }
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_MEDIA);
+ mnl_attr_put_strz(nlh, TIPC_NLA_MEDIA_NAME, opt->val);
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, media_get_cb, &prop);
+}
+
+static void cmd_media_get_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s media get PPROPERTY media MEDIA\n\n"
+ "PROPERTIES\n"
+ " tolerance - Get media tolerance\n"
+ " priority - Get media priority\n"
+ " window - Get media window\n"
+ " mtu - Get media mtu\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_media_get(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "priority", cmd_media_get_prop, cmd_media_get_help },
+ { "tolerance", cmd_media_get_prop, cmd_media_get_help },
+ { "window", cmd_media_get_prop, cmd_media_get_help },
+ { "mtu", cmd_media_get_prop, cmd_media_get_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_media_set_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s media set PPROPERTY media MEDIA\n\n"
+ "PROPERTIES\n"
+ " tolerance TOLERANCE - Set media tolerance\n"
+ " priority PRIORITY - Set media priority\n"
+ " window WINDOW - Set media window\n"
+ " mtu MTU - Set media mtu\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_media_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int val;
+ int prop;
+ struct nlattr *props;
+ struct nlattr *attrs;
+ struct opt *opt;
+ struct opt opts[] = {
+ { "media", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+
+ if (strcmp(cmd->cmd, "priority") == 0)
+ prop = TIPC_NLA_PROP_PRIO;
+ else if ((strcmp(cmd->cmd, "tolerance") == 0))
+ prop = TIPC_NLA_PROP_TOL;
+ else if ((strcmp(cmd->cmd, "window") == 0))
+ prop = TIPC_NLA_PROP_WIN;
+ else if ((strcmp(cmd->cmd, "mtu") == 0))
+ prop = TIPC_NLA_PROP_MTU;
+ else
+ return -EINVAL;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ if (cmdl->optind >= cmdl->argc) {
+ fprintf(stderr, "error, missing value\n");
+ return -EINVAL;
+ }
+ val = atoi(shift_cmdl(cmdl));
+
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ nlh = msg_init(TIPC_NL_MEDIA_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ attrs = mnl_attr_nest_start(nlh, TIPC_NLA_MEDIA);
+
+ if (!(opt = get_opt(opts, "media"))) {
+ fprintf(stderr, "error, missing media\n");
+ return -EINVAL;
+ }
+
+ if ((prop == TIPC_NLA_PROP_MTU) &&
+ (strcmp(opt->val, "udp"))) {
+ fprintf(stderr, "error, not supported for media\n");
+ return -EINVAL;
+ }
+ mnl_attr_put_strz(nlh, TIPC_NLA_MEDIA_NAME, opt->val);
+
+ props = mnl_attr_nest_start(nlh, TIPC_NLA_MEDIA_PROP);
+ mnl_attr_put_u32(nlh, prop, val);
+ mnl_attr_nest_end(nlh, props);
+
+ mnl_attr_nest_end(nlh, attrs);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int cmd_media_set(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "priority", cmd_media_set_prop, cmd_media_set_help },
+ { "tolerance", cmd_media_set_prop, cmd_media_set_help },
+ { "window", cmd_media_set_prop, cmd_media_set_help },
+ { "mtu", cmd_media_set_prop, cmd_media_set_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+void cmd_media_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s media COMMAND [ARGS] ...\n"
+ "\n"
+ "Commands:\n"
+ " list - List active media types\n"
+ " get - Get various media properties\n"
+ " set - Set various media properties\n",
+ cmdl->argv[0]);
+}
+
+int cmd_media(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "get", cmd_media_get, cmd_media_get_help },
+ { "list", cmd_media_list, NULL },
+ { "set", cmd_media_set, cmd_media_set_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
diff --git a/tipc/media.h b/tipc/media.h
new file mode 100644
index 0000000..f1b4b54
--- /dev/null
+++ b/tipc/media.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * media.h TIPC link functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_MEDIA_H
+#define _TIPC_MEDIA_H
+
+extern int help_flag;
+
+int cmd_media(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data);
+void cmd_media_help(struct cmdl *cmdl);
+
+#endif
diff --git a/tipc/misc.c b/tipc/misc.c
new file mode 100644
index 0000000..32d4a5e
--- /dev/null
+++ b/tipc/misc.c
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * misc.c Miscellaneous TIPC helper functions.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <linux/tipc.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+#include "misc.h"
+
+#define IN_RANGE(val, low, high) ((val) <= (high) && (val) >= (low))
+
+uint32_t str2addr(char *str)
+{
+ unsigned int z, c, n;
+ char dummy;
+
+ if (sscanf(str, "%u.%u.%u%c", &z, &c, &n, &dummy) != 3) {
+ fprintf(stderr, "invalid network address, syntax: Z.C.N\n");
+ return 0;
+ }
+
+ if (IN_RANGE(z, 0, 255) && IN_RANGE(c, 0, 4095) && IN_RANGE(n, 0, 4095))
+ return tipc_addr(z, c, n);
+
+ fprintf(stderr, "invalid network address \"%s\"\n", str);
+ return 0;
+}
+
+static int is_hex(char *arr, int last)
+{
+ int i;
+
+ while (!arr[last])
+ last--;
+
+ for (i = 0; i <= last; i++) {
+ if (!IN_RANGE(arr[i], '0', '9') &&
+ !IN_RANGE(arr[i], 'a', 'f') &&
+ !IN_RANGE(arr[i], 'A', 'F'))
+ return 0;
+ }
+ return 1;
+}
+
+static int is_name(char *arr, int last)
+{
+ int i;
+ char c;
+
+ while (!arr[last])
+ last--;
+
+ if (last > 15)
+ return 0;
+
+ for (i = 0; i <= last; i++) {
+ c = arr[i];
+ if (!IN_RANGE(c, '0', '9') && !IN_RANGE(c, 'a', 'z') &&
+ !IN_RANGE(c, 'A', 'Z') && c != '-' && c != '_' &&
+ c != '.' && c != ':' && c != '@')
+ return 0;
+ }
+ return 1;
+}
+
+int str2nodeid(char *str, uint8_t *id)
+{
+ int len = strlen(str);
+ int i;
+
+ if (len > 32)
+ return -1;
+
+ if (is_name(str, len - 1)) {
+ memcpy(id, str, len);
+ return 0;
+ }
+ if (!is_hex(str, len - 1))
+ return -1;
+
+ str[len] = '0';
+ for (i = 0; i < 16; i++) {
+ if (sscanf(&str[2 * i], "%2hhx", &id[i]) != 1)
+ break;
+ }
+ return 0;
+}
+
+int str2key(char *str, struct tipc_aead_key *key)
+{
+ int len = strlen(str);
+ int ishex = 0;
+ int i;
+
+ /* Check if the input is a hex string (i.e. 0x...) */
+ if (len > 2 && strncmp(str, "0x", 2) == 0) {
+ ishex = is_hex(str + 2, len - 2 - 1);
+ if (ishex) {
+ len -= 2;
+ str += 2;
+ }
+ }
+
+ key->keylen = ishex ? (len + 1) / 2 : len;
+ if (key->keylen > TIPC_AEAD_KEYLEN_MAX)
+ return -1;
+
+ /* Obtain key: */
+ if (!ishex) {
+ memcpy(key->key, str, len);
+ } else {
+ /* Convert hex string to key */
+ for (i = 0; i < key->keylen; i++) {
+ if (i == 0 && len % 2 != 0) {
+ if (sscanf(str, "%1hhx", &key->key[0]) != 1)
+ return -1;
+ str += 1;
+ continue;
+ }
+ if (sscanf(str, "%2hhx", &key->key[i]) != 1)
+ return -1;
+ str += 2;
+ }
+ }
+
+ return 0;
+}
+
+void nodeid2str(uint8_t *id, char *str)
+{
+ int i;
+
+ if (is_name((char *)id, 15)) {
+ memcpy(str, id, 16);
+ return;
+ }
+
+ for (i = 0; i < 16; i++)
+ sprintf(&str[2 * i], "%02x", id[i]);
+
+ for (i = 31; str[i] == '0'; i--)
+ str[i] = 0;
+}
+
+void hash2nodestr(uint32_t hash, char *str)
+{
+ struct tipc_sioc_nodeid_req nr = {};
+ int sd;
+
+ sd = socket(AF_TIPC, SOCK_RDM, 0);
+ if (sd < 0) {
+ fprintf(stderr, "opening TIPC socket: %s\n", strerror(errno));
+ return;
+ }
+ nr.peer = hash;
+ if (!ioctl(sd, SIOCGETNODEID, &nr))
+ nodeid2str((uint8_t *)nr.node_id, str);
+ close(sd);
+}
diff --git a/tipc/misc.h b/tipc/misc.h
new file mode 100644
index 0000000..d00d7c9
--- /dev/null
+++ b/tipc/misc.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * misc.h Miscellaneous TIPC helper functions.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_MISC_H
+#define _TIPC_MISC_H
+
+#include <stdint.h>
+
+uint32_t str2addr(char *str);
+int str2nodeid(char *str, uint8_t *id);
+void nodeid2str(uint8_t *id, char *str);
+void hash2nodestr(uint32_t hash, char *str);
+int str2key(char *str, struct tipc_aead_key *key);
+
+#endif
diff --git a/tipc/msg.c b/tipc/msg.c
new file mode 100644
index 0000000..731b0fa
--- /dev/null
+++ b/tipc/msg.c
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * msg.c Messaging (netlink) helper functions.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+
+#include "mnl_utils.h"
+#include "msg.h"
+
+extern struct mnlu_gen_socket tipc_nlg;
+
+int parse_attrs(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ tb[type] = attr;
+
+ return MNL_CB_OK;
+}
+
+int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
+{
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ return mnlu_gen_socket_sndrcv(&tipc_nlg, nlh, callback, data);
+}
+
+int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
+{
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ return mnlu_gen_socket_sndrcv(&tipc_nlg, nlh, callback, data);
+}
+
+struct nlmsghdr *msg_init(int cmd)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&tipc_nlg, cmd, 0);
+
+ return nlh;
+}
diff --git a/tipc/msg.h b/tipc/msg.h
new file mode 100644
index 0000000..118a266
--- /dev/null
+++ b/tipc/msg.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * msg.h Messaging (netlink) helper functions.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_MSG_H
+#define _TIPC_MSG_H
+
+struct nlmsghdr *msg_init(int cmd);
+int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data);
+int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data);
+int parse_attrs(const struct nlattr *attr, void *data);
+
+#endif
diff --git a/tipc/nametable.c b/tipc/nametable.c
new file mode 100644
index 0000000..5162f7f
--- /dev/null
+++ b/tipc/nametable.c
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * nametable.c TIPC nametable functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <linux/tipc_netlink.h>
+#include <linux/tipc.h>
+#include <linux/genetlink.h>
+
+#include "cmdl.h"
+#include "msg.h"
+#include "nametable.h"
+#include "misc.h"
+#include "utils.h"
+
+#define PORTID_STR_LEN 45 /* Four u32 and five delimiter chars */
+
+static int nametable_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int *iteration = data;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_NAME_TABLE_MAX + 1] = {};
+ struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1] = {};
+ const char *scope[] = { "", "zone", "cluster", "node" };
+ char str[33] = {0,};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_NAME_TABLE])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_NAME_TABLE], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_NAME_TABLE_PUBL])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_NAME_TABLE_PUBL], parse_attrs, publ);
+ if (!publ[TIPC_NLA_NAME_TABLE_PUBL])
+ return MNL_CB_ERROR;
+
+ if (!*iteration && !is_json_context())
+ printf("%-10s %-10s %-10s %-8s %-10s %-33s\n",
+ "Type", "Lower", "Upper", "Scope", "Port",
+ "Node");
+ (*iteration)++;
+
+ hash2nodestr(mnl_attr_get_u32(publ[TIPC_NLA_PUBL_NODE]), str);
+
+ open_json_object(NULL);
+ print_uint(PRINT_ANY, "type", "%-10u",
+ mnl_attr_get_u32(publ[TIPC_NLA_PUBL_TYPE]));
+ print_string(PRINT_FP, NULL, " ", "");
+ print_uint(PRINT_ANY, "lower", "%-10u",
+ mnl_attr_get_u32(publ[TIPC_NLA_PUBL_LOWER]));
+ print_string(PRINT_FP, NULL, " ", "");
+ print_uint(PRINT_ANY, "upper", "%-10u",
+ mnl_attr_get_u32(publ[TIPC_NLA_PUBL_UPPER]));
+ print_string(PRINT_FP, NULL, " ", "");
+ print_string(PRINT_ANY, "scope", "%-8s",
+ scope[mnl_attr_get_u32(publ[TIPC_NLA_PUBL_SCOPE])]);
+ print_string(PRINT_FP, NULL, " ", "");
+ print_uint(PRINT_ANY, "port", "%-10u",
+ mnl_attr_get_u32(publ[TIPC_NLA_PUBL_REF]));
+ print_string(PRINT_FP, NULL, " ", "");
+ print_string(PRINT_ANY, "node", "%s", str);
+ print_string(PRINT_FP, NULL, "\n", "");
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+static int cmd_nametable_show(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int iteration = 0;
+ int rc = 0;
+
+ if (help_flag) {
+ fprintf(stderr, "Usage: %s nametable show\n", cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_NAME_TABLE_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ new_json_obj(json);
+ rc = msg_dumpit(nlh, nametable_show_cb, &iteration);
+ delete_json_obj();
+
+ return rc;
+}
+
+void cmd_nametable_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s nametable COMMAND\n\n"
+ "COMMANDS\n"
+ " show - Show nametable\n",
+ cmdl->argv[0]);
+}
+
+int cmd_nametable(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "show", cmd_nametable_show, NULL },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
diff --git a/tipc/nametable.h b/tipc/nametable.h
new file mode 100644
index 0000000..c4df8d9
--- /dev/null
+++ b/tipc/nametable.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * nametable.h TIPC nametable functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_NAMETABLE_H
+#define _TIPC_NAMETABLE_H
+
+extern int help_flag;
+
+void cmd_nametable_help(struct cmdl *cmdl);
+int cmd_nametable(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data);
+
+#endif
diff --git a/tipc/node.c b/tipc/node.c
new file mode 100644
index 0000000..e645d37
--- /dev/null
+++ b/tipc/node.c
@@ -0,0 +1,505 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * node.c TIPC node functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/tipc_netlink.h>
+#include <linux/tipc.h>
+#include <linux/genetlink.h>
+
+#include "cmdl.h"
+#include "msg.h"
+#include "misc.h"
+#include "node.h"
+
+static int node_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_NODE_MAX + 1] = {};
+ char str[33] = {};
+ uint32_t addr;
+
+ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_attrs, info);
+ if (!info[TIPC_NLA_NODE])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_NODE], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_NODE_ADDR])
+ return MNL_CB_ERROR;
+
+ addr = mnl_attr_get_u32(attrs[TIPC_NLA_NODE_ADDR]);
+ hash2nodestr(addr, str);
+ printf("%-32s %08x ", str, addr);
+ if (attrs[TIPC_NLA_NODE_UP])
+ printf("up\n");
+ else
+ printf("down\n");
+ return MNL_CB_OK;
+}
+
+static int cmd_node_list(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ if (help_flag) {
+ fprintf(stderr, "Usage: %s node list\n", cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_NODE_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ printf("Node Identity Hash State\n");
+ return msg_dumpit(nlh, node_list_cb, NULL);
+}
+
+static int cmd_node_set_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ char *str;
+ uint32_t addr;
+ struct nlattr *nest;
+
+ if (cmdl->argc != cmdl->optind + 1) {
+ fprintf(stderr, "Usage: %s node set address ADDRESS\n",
+ cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ str = shift_cmdl(cmdl);
+ addr = str2addr(str);
+ if (!addr)
+ return -1;
+
+ nlh = msg_init(TIPC_NL_NET_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET);
+ mnl_attr_put_u32(nlh, TIPC_NLA_NET_ADDR, addr);
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int cmd_node_get_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int sk;
+ socklen_t sz = sizeof(struct sockaddr_tipc);
+ struct sockaddr_tipc addr;
+
+ sk = socket(AF_TIPC, SOCK_RDM, 0);
+ if (sk < 0) {
+ fprintf(stderr, "opening TIPC socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (getsockname(sk, (struct sockaddr *)&addr, &sz) < 0) {
+ fprintf(stderr, "getting TIPC socket address: %s\n",
+ strerror(errno));
+ close(sk);
+ return -1;
+ }
+ close(sk);
+
+ printf("%08x\n", addr.addr.id.node);
+ return 0;
+}
+
+static int cmd_node_set_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ uint8_t id[16] = {0,};
+ uint64_t *w0 = (uint64_t *) &id[0];
+ uint64_t *w1 = (uint64_t *) &id[8];
+ struct nlattr *nest;
+ char *str;
+
+ if (cmdl->argc != cmdl->optind + 1) {
+ fprintf(stderr, "Usage: %s node set nodeid NODE_ID\n",
+ cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ str = shift_cmdl(cmdl);
+ if (str2nodeid(str, id)) {
+ fprintf(stderr, "Invalid node identity\n");
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_NET_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET);
+ mnl_attr_put_u64(nlh, TIPC_NLA_NET_NODEID, *w0);
+ mnl_attr_put_u64(nlh, TIPC_NLA_NET_NODEID_W1, *w1);
+ mnl_attr_nest_end(nlh, nest);
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static void cmd_node_set_key_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s node set key KEY [algname ALGNAME] [PROPERTIES]\n"
+ " %s node set key rekeying REKEYING\n\n"
+ "KEY\n"
+ " Symmetric KEY & SALT as a composite ASCII or hex string (0x...) in form:\n"
+ " [KEY: 16, 24 or 32 octets][SALT: 4 octets]\n\n"
+ "ALGNAME\n"
+ " Cipher algorithm [default: \"gcm(aes)\"]\n\n"
+ "PROPERTIES\n"
+ " master - Set KEY as a cluster master key\n"
+ " <empty> - Set KEY as a cluster key\n"
+ " nodeid NODEID - Set KEY as a per-node key for own or peer\n\n"
+ "REKEYING\n"
+ " INTERVAL - Set rekeying interval (in minutes) [0: disable]\n"
+ " now - Trigger one (first) rekeying immediately\n\n"
+ "EXAMPLES\n"
+ " %s node set key this_is_a_master_key master\n"
+ " %s node set key 0x746869735F69735F615F6B657931365F73616C74\n"
+ " %s node set key this_is_a_key16_salt algname \"gcm(aes)\" nodeid 1001002\n"
+ " %s node set key rekeying 600\n\n",
+ cmdl->argv[0], cmdl->argv[0], cmdl->argv[0], cmdl->argv[0],
+ cmdl->argv[0], cmdl->argv[0]);
+}
+
+static int cmd_node_set_key(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ struct {
+ union {
+ struct tipc_aead_key key;
+ char mem[TIPC_AEAD_KEY_SIZE_MAX];
+ };
+ } input = {};
+ struct opt opts[] = {
+ { "algname", OPT_KEYVAL, NULL },
+ { "nodeid", OPT_KEYVAL, NULL },
+ { "master", OPT_KEY, NULL },
+ { "rekeying", OPT_KEYVAL, NULL },
+ { NULL }
+ };
+ struct nlattr *nest;
+ struct opt *opt_algname, *opt_nodeid, *opt_master, *opt_rekeying;
+ uint8_t id[TIPC_NODEID_LEN] = {0,};
+ uint32_t rekeying = 0;
+ bool has_key = false;
+ int keysize;
+ char *str;
+
+ if (help_flag || cmdl->optind >= cmdl->argc) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ /* Check if command starts with opts i.e. "rekeying" opt without key */
+ if (find_opt(opts, cmdl->argv[cmdl->optind]))
+ goto get_ops;
+
+ /* Get user key */
+ has_key = true;
+ str = shift_cmdl(cmdl);
+ if (str2key(str, &input.key)) {
+ fprintf(stderr, "error, invalid key input\n");
+ return -EINVAL;
+ }
+
+get_ops:
+ if (parse_opts(opts, cmdl) < 0)
+ return -EINVAL;
+
+ /* Get rekeying time */
+ opt_rekeying = get_opt(opts, "rekeying");
+ if (opt_rekeying) {
+ if (!strcmp(opt_rekeying->val, "now"))
+ rekeying = TIPC_REKEYING_NOW;
+ else
+ rekeying = atoi(opt_rekeying->val);
+ }
+
+ /* Get algorithm name, default: "gcm(aes)" */
+ opt_algname = get_opt(opts, "algname");
+ if (!opt_algname) {
+ strcpy(input.key.alg_name, "gcm(aes)");
+ } else {
+ if (strlen(opt_algname->val) > TIPC_AEAD_ALG_NAME) {
+ fprintf(stderr, "error, invalid algname\n");
+ return -EINVAL;
+ }
+ strcpy(input.key.alg_name, opt_algname->val);
+ }
+
+ /* Get node identity */
+ opt_nodeid = get_opt(opts, "nodeid");
+ if (opt_nodeid && str2nodeid(opt_nodeid->val, id)) {
+ fprintf(stderr, "error, invalid node identity\n");
+ return -EINVAL;
+ }
+
+ /* Get master key indication */
+ opt_master = get_opt(opts, "master");
+
+ /* Sanity check if wrong option */
+ if (opt_nodeid && opt_master) {
+ fprintf(stderr, "error, per-node key cannot be master\n");
+ return -EINVAL;
+ }
+
+ /* Init & do the command */
+ nlh = msg_init(TIPC_NL_KEY_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_NODE);
+ if (has_key) {
+ keysize = tipc_aead_key_size(&input.key);
+ mnl_attr_put(nlh, TIPC_NLA_NODE_KEY, keysize, &input.key);
+ if (opt_nodeid)
+ mnl_attr_put(nlh, TIPC_NLA_NODE_ID, TIPC_NODEID_LEN, id);
+ if (opt_master)
+ mnl_attr_put(nlh, TIPC_NLA_NODE_KEY_MASTER, 0, NULL);
+ }
+ if (opt_rekeying)
+ mnl_attr_put_u32(nlh, TIPC_NLA_NODE_REKEYING, rekeying);
+
+ mnl_attr_nest_end(nlh, nest);
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int cmd_node_flush_key(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ /* Init & do the command */
+ nlh = msg_init(TIPC_NL_KEY_FLUSH);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int nodeid_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_NET_MAX + 1] = {};
+ char str[33] = {0,};
+ uint8_t id[16] = {0,};
+ uint64_t *w0 = (uint64_t *) &id[0];
+ uint64_t *w1 = (uint64_t *) &id[8];
+
+ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_attrs, info);
+ if (!info[TIPC_NLA_NET])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_NET], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_NET_ID])
+ return MNL_CB_ERROR;
+
+ *w0 = mnl_attr_get_u64(attrs[TIPC_NLA_NET_NODEID]);
+ *w1 = mnl_attr_get_u64(attrs[TIPC_NLA_NET_NODEID_W1]);
+ nodeid2str(id, str);
+ printf("Node Identity Hash\n");
+ printf("%-33s", str);
+ cmd_node_get_addr(NULL, NULL, NULL, NULL);
+ return MNL_CB_OK;
+}
+
+static int cmd_node_get_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_NET_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ return msg_dumpit(nlh, nodeid_get_cb, NULL);
+}
+
+
+static int netid_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_NET_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_attrs, info);
+ if (!info[TIPC_NLA_NET])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_NET], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_NET_ID])
+ return MNL_CB_ERROR;
+
+ printf("%u\n", mnl_attr_get_u32(attrs[TIPC_NLA_NET_ID]));
+
+ return MNL_CB_OK;
+}
+
+static int cmd_node_get_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_NET_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ return msg_dumpit(nlh, netid_get_cb, NULL);
+}
+
+static int cmd_node_set_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ int netid;
+ struct nlattr *nest;
+
+ if (help_flag) {
+ (cmd->help)(cmdl);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_NET_SET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ if (cmdl->argc != cmdl->optind + 1) {
+ fprintf(stderr, "Usage: %s node set netid NETID\n",
+ cmdl->argv[0]);
+ return -EINVAL;
+ }
+ netid = atoi(shift_cmdl(cmdl));
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET);
+ mnl_attr_put_u32(nlh, TIPC_NLA_NET_ID, netid);
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static void cmd_node_flush_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s node flush PROPERTY\n\n"
+ "PROPERTIES\n"
+ " key - Flush all symmetric-keys\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_node_flush(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "key", cmd_node_flush_key, NULL },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_node_set_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s node set PROPERTY\n\n"
+ "PROPERTIES\n"
+ " identity NODEID - Set node identity\n"
+ " clusterid CLUSTERID - Set local cluster id\n"
+ " key PROPERTY - Set symmetric-key\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_node_set(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "address", cmd_node_set_addr, NULL },
+ { "identity", cmd_node_set_nodeid, NULL },
+ { "netid", cmd_node_set_netid, NULL },
+ { "clusterid", cmd_node_set_netid, NULL },
+ { "key", cmd_node_set_key, cmd_node_set_key_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+static void cmd_node_get_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s node get PROPERTY\n\n"
+ "PROPERTIES\n"
+ " identity - Get node identity\n"
+ " clusterid - Get local clusterid\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_node_get(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "address", cmd_node_get_addr, NULL },
+ { "identity", cmd_node_get_nodeid, NULL },
+ { "netid", cmd_node_get_netid, NULL },
+ { "clusterid", cmd_node_get_netid, NULL },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+void cmd_node_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s node COMMAND [ARGS] ...\n\n"
+ "COMMANDS\n"
+ " list - List remote nodes\n"
+ " get - Get local node parameters\n"
+ " set - Set local node parameters\n"
+ " flush - Flush local node parameters\n",
+ cmdl->argv[0]);
+}
+
+int cmd_node(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "list", cmd_node_list, NULL },
+ { "get", cmd_node_get, cmd_node_get_help },
+ { "set", cmd_node_set, cmd_node_set_help },
+ { "flush", cmd_node_flush, cmd_node_flush_help},
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
diff --git a/tipc/node.h b/tipc/node.h
new file mode 100644
index 0000000..4a986d0
--- /dev/null
+++ b/tipc/node.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * node.h TIPC node functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_NODE_H
+#define _TIPC_NODE_H
+
+extern int help_flag;
+
+int cmd_node(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data);
+void cmd_node_help(struct cmdl *cmdl);
+
+#endif
diff --git a/tipc/peer.c b/tipc/peer.c
new file mode 100644
index 0000000..5a583fb
--- /dev/null
+++ b/tipc/peer.c
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * peer.c TIPC peer functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/tipc_netlink.h>
+#include <linux/tipc.h>
+#include <linux/genetlink.h>
+
+#include "cmdl.h"
+#include "msg.h"
+#include "misc.h"
+#include "peer.h"
+
+static int cmd_peer_rm_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ char *str;
+ uint32_t addr;
+ struct nlattr *nest;
+
+ if ((cmdl->argc != cmdl->optind + 1) || help_flag) {
+ fprintf(stderr, "Usage: %s peer remove address ADDRESS\n",
+ cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ str = shift_cmdl(cmdl);
+
+ /* First try legacy Z.C.N format, then integer format */
+ addr = str2addr(str);
+ if (!addr)
+ addr = atoi(str);
+ if (!addr)
+ return -1;
+
+ nlh = msg_init(TIPC_NL_PEER_REMOVE);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET);
+ mnl_attr_put_u32(nlh, TIPC_NLA_NET_ADDR, addr);
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static int cmd_peer_rm_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ __u8 id[16] = {0,};
+ __u64 *w0 = (__u64 *)&id[0];
+ __u64 *w1 = (__u64 *)&id[8];
+ struct nlattr *nest;
+ char *str;
+
+ if (cmdl->argc != cmdl->optind + 1) {
+ fprintf(stderr, "Usage: %s peer remove identity NODEID\n",
+ cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ str = shift_cmdl(cmdl);
+ if (str2nodeid(str, id)) {
+ fprintf(stderr, "Invalid node identity\n");
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_PEER_REMOVE);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET);
+ mnl_attr_put_u64(nlh, TIPC_NLA_NET_NODEID, *w0);
+ mnl_attr_put_u64(nlh, TIPC_NLA_NET_NODEID_W1, *w1);
+ mnl_attr_nest_end(nlh, nest);
+
+ return msg_doit(nlh, NULL, NULL);
+}
+
+static void cmd_peer_rm_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s peer remove PROPERTY\n\n"
+ "PROPERTIES\n"
+ " identity NODEID - Remove peer node identity\n",
+ cmdl->argv[0]);
+}
+
+static void cmd_peer_rm_addr_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s peer remove address ADDRESS\n",
+ cmdl->argv[0]);
+}
+
+static void cmd_peer_rm_nodeid_help(struct cmdl *cmdl)
+{
+ fprintf(stderr, "Usage: %s peer remove identity NODEID\n",
+ cmdl->argv[0]);
+}
+
+static int cmd_peer_rm(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ const struct cmd cmds[] = {
+ { "address", cmd_peer_rm_addr, cmd_peer_rm_addr_help },
+ { "identity", cmd_peer_rm_nodeid, cmd_peer_rm_nodeid_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
+
+void cmd_peer_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s peer COMMAND [ARGS] ...\n\n"
+ "COMMANDS\n"
+ " remove - Remove an offline peer node\n",
+ cmdl->argv[0]);
+}
+
+int cmd_peer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "remove", cmd_peer_rm, cmd_peer_rm_help },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
diff --git a/tipc/peer.h b/tipc/peer.h
new file mode 100644
index 0000000..2bd0a2a
--- /dev/null
+++ b/tipc/peer.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * peer.h TIPC peer functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_PEER_H
+#define _TIPC_PEER_H
+
+extern int help_flag;
+
+int cmd_peer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data);
+void cmd_peer_help(struct cmdl *cmdl);
+
+#endif
diff --git a/tipc/socket.c b/tipc/socket.c
new file mode 100644
index 0000000..4d376e0
--- /dev/null
+++ b/tipc/socket.c
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * socket.c TIPC socket functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <linux/tipc.h>
+#include <linux/tipc_netlink.h>
+#include <linux/genetlink.h>
+#include <libmnl/libmnl.h>
+
+#include "mnl_utils.h"
+#include "cmdl.h"
+#include "msg.h"
+#include "socket.h"
+
+#define PORTID_STR_LEN 45 /* Four u32 and five delimiter chars */
+
+static int publ_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_SOCK_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_PUBL])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_PUBL], parse_attrs, attrs);
+
+ printf(" bound to {%u,%u,%u}\n",
+ mnl_attr_get_u32(attrs[TIPC_NLA_PUBL_TYPE]),
+ mnl_attr_get_u32(attrs[TIPC_NLA_PUBL_LOWER]),
+ mnl_attr_get_u32(attrs[TIPC_NLA_PUBL_UPPER]));
+
+ return MNL_CB_OK;
+}
+
+static int publ_list(uint32_t sock)
+{
+ struct mnlu_gen_socket sock_nlg;
+ struct nlmsghdr *nlh;
+ struct nlattr *nest;
+ int err;
+
+ err = mnlu_gen_socket_open(&sock_nlg, TIPC_GENL_V2_NAME,
+ TIPC_GENL_V2_VERSION);
+ if (err)
+ return -1;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&sock_nlg, TIPC_NL_PUBL_GET,
+ NLM_F_REQUEST | NLM_F_DUMP);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ mnlu_gen_socket_close(&sock_nlg);
+ return -1;
+ }
+
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_SOCK);
+ mnl_attr_put_u32(nlh, TIPC_NLA_SOCK_REF, sock);
+ mnl_attr_nest_end(nlh, nest);
+
+ err = mnlu_gen_socket_sndrcv(&sock_nlg, nlh, publ_list_cb, NULL);
+ mnlu_gen_socket_close(&sock_nlg);
+ return err;
+}
+
+static int sock_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *info[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *attrs[TIPC_NLA_SOCK_MAX + 1] = {};
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_SOCK])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_SOCK], parse_attrs, attrs);
+ if (!attrs[TIPC_NLA_SOCK_REF])
+ return MNL_CB_ERROR;
+
+ printf("socket %u\n", mnl_attr_get_u32(attrs[TIPC_NLA_SOCK_REF]));
+
+ if (attrs[TIPC_NLA_SOCK_CON]) {
+ uint32_t node;
+ struct nlattr *con[TIPC_NLA_CON_MAX + 1] = {};
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_SOCK_CON], parse_attrs, con);
+ node = mnl_attr_get_u32(con[TIPC_NLA_CON_NODE]);
+
+ printf(" connected to %x:%u", node,
+ mnl_attr_get_u32(con[TIPC_NLA_CON_SOCK]));
+
+ if (con[TIPC_NLA_CON_FLAG])
+ printf(" via {%u,%u}\n",
+ mnl_attr_get_u32(con[TIPC_NLA_CON_TYPE]),
+ mnl_attr_get_u32(con[TIPC_NLA_CON_INST]));
+ else
+ printf("\n");
+ } else if (attrs[TIPC_NLA_SOCK_HAS_PUBL]) {
+ publ_list(mnl_attr_get_u32(attrs[TIPC_NLA_SOCK_REF]));
+ }
+
+ return MNL_CB_OK;
+}
+
+static int cmd_socket_list(struct nlmsghdr *nlh, const struct cmd *cmd,
+ struct cmdl *cmdl, void *data)
+{
+ if (help_flag) {
+ fprintf(stderr, "Usage: %s socket list\n", cmdl->argv[0]);
+ return -EINVAL;
+ }
+
+ nlh = msg_init(TIPC_NL_SOCK_GET);
+ if (!nlh) {
+ fprintf(stderr, "error, message initialisation failed\n");
+ return -1;
+ }
+
+ return msg_dumpit(nlh, sock_list_cb, NULL);
+}
+
+void cmd_socket_help(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Usage: %s socket COMMAND\n\n"
+ "Commands:\n"
+ " list - List sockets (ports)\n",
+ cmdl->argv[0]);
+}
+
+int cmd_socket(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data)
+{
+ const struct cmd cmds[] = {
+ { "list", cmd_socket_list, NULL },
+ { NULL }
+ };
+
+ return run_cmd(nlh, cmd, cmds, cmdl, NULL);
+}
diff --git a/tipc/socket.h b/tipc/socket.h
new file mode 100644
index 0000000..c4341bb
--- /dev/null
+++ b/tipc/socket.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * socket.h TIPC socket functionality.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#ifndef _TIPC_SOCKET_H
+#define _TIPC_SOCKET_H
+
+extern int help_flag;
+
+void cmd_socket_help(struct cmdl *cmdl);
+int cmd_socket(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
+ void *data);
+
+#endif
diff --git a/tipc/tipc.c b/tipc/tipc.c
new file mode 100644
index 0000000..56af052
--- /dev/null
+++ b/tipc/tipc.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * tipc. TIPC utility frontend.
+ *
+ * Authors: Richard Alpe <richard.alpe@ericsson.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <linux/tipc_netlink.h>
+#include <libmnl/libmnl.h>
+#include <errno.h>
+
+#include "mnl_utils.h"
+#include "bearer.h"
+#include "link.h"
+#include "nametable.h"
+#include "socket.h"
+#include "media.h"
+#include "node.h"
+#include "peer.h"
+#include "cmdl.h"
+#include "utils.h"
+
+int help_flag;
+int json;
+struct mnlu_gen_socket tipc_nlg;
+
+static void about(struct cmdl *cmdl)
+{
+ fprintf(stderr,
+ "Transparent Inter-Process Communication Protocol\n"
+ "Usage: %s [OPTIONS] COMMAND [ARGS] ...\n"
+ "\n"
+ "Options:\n"
+ " -h, --help \t\tPrint help for last given command\n"
+ " -j, --json \t\tJson format printouts\n"
+ " -p, --pretty \t\tpretty print\n"
+ "\n"
+ "Commands:\n"
+ " bearer - Show or modify bearers\n"
+ " link - Show or modify links\n"
+ " media - Show or modify media\n"
+ " nametable - Show nametable\n"
+ " node - Show or modify node related parameters\n"
+ " peer - Peer related operations\n"
+ " socket - Show sockets\n",
+ cmdl->argv[0]);
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ int res;
+ struct cmdl cmdl;
+ const struct cmd cmd = {"tipc", NULL, about};
+ struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"json", no_argument, 0, 'j'},
+ {"pretty", no_argument, 0, 'p'},
+ {0, 0, 0, 0}
+ };
+ const struct cmd cmds[] = {
+ { "bearer", cmd_bearer, cmd_bearer_help},
+ { "link", cmd_link, cmd_link_help},
+ { "media", cmd_media, cmd_media_help},
+ { "nametable", cmd_nametable, cmd_nametable_help},
+ { "node", cmd_node, cmd_node_help},
+ { "peer", cmd_peer, cmd_peer_help},
+ { "socket", cmd_socket, cmd_socket_help},
+ { NULL }
+ };
+
+ do {
+ int option_index = 0;
+
+ i = getopt_long(argc, argv, "hjp", long_options, &option_index);
+
+ switch (i) {
+ case 'h':
+ /*
+ * We want the help for the last command, so we flag
+ * here in order to print later.
+ */
+ help_flag = 1;
+ break;
+ case 'j':
+ /*
+ * Enable json format printouts
+ */
+ json = 1;
+ break;
+ case 'p':
+ /*
+ * Enable json pretty output
+ */
+ pretty = 1;
+ break;
+ case -1:
+ /* End of options */
+ break;
+ default:
+ /* Invalid option, error msg is printed by getopts */
+ return 1;
+ }
+ } while (i != -1);
+
+ cmdl.optind = optind;
+ cmdl.argc = argc;
+ cmdl.argv = argv;
+
+ res = mnlu_gen_socket_open(&tipc_nlg, TIPC_GENL_V2_NAME,
+ TIPC_GENL_V2_VERSION);
+ if (res) {
+ fprintf(stderr,
+ "Unable to get TIPC nl family id (module loaded?)\n");
+ return -1;
+ }
+
+ res = run_cmd(NULL, &cmd, cmds, &cmdl, &tipc_nlg);
+ if (res != 0) {
+ mnlu_gen_socket_close(&tipc_nlg);
+ return -1;
+ }
+
+ mnlu_gen_socket_close(&tipc_nlg);
+ return 0;
+}