diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:14:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:14:35 +0000 |
commit | 9b8a97db9ec4b795e29e72289005fbc58484ebeb (patch) | |
tree | e24ca2d68215e57b4759fe5c032629821eabb250 /tipc | |
parent | Initial commit. (diff) | |
download | iproute2-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/.gitignore | 1 | ||||
-rw-r--r-- | tipc/Makefile | 24 | ||||
-rw-r--r-- | tipc/README | 63 | ||||
-rw-r--r-- | tipc/bearer.c | 1134 | ||||
-rw-r--r-- | tipc/bearer.h | 22 | ||||
-rw-r--r-- | tipc/cmdl.c | 130 | ||||
-rw-r--r-- | tipc/cmdl.h | 54 | ||||
-rw-r--r-- | tipc/link.c | 1248 | ||||
-rw-r--r-- | tipc/link.h | 17 | ||||
-rw-r--r-- | tipc/media.c | 273 | ||||
-rw-r--r-- | tipc/media.h | 17 | ||||
-rw-r--r-- | tipc/misc.c | 167 | ||||
-rw-r--r-- | tipc/misc.h | 19 | ||||
-rw-r--r-- | tipc/msg.c | 48 | ||||
-rw-r--r-- | tipc/msg.h | 16 | ||||
-rw-r--r-- | tipc/nametable.c | 118 | ||||
-rw-r--r-- | tipc/nametable.h | 17 | ||||
-rw-r--r-- | tipc/node.c | 505 | ||||
-rw-r--r-- | tipc/node.h | 17 | ||||
-rw-r--r-- | tipc/peer.c | 142 | ||||
-rw-r--r-- | tipc/peer.h | 17 | ||||
-rw-r--r-- | tipc/socket.c | 146 | ||||
-rw-r--r-- | tipc/socket.h | 17 | ||||
-rw-r--r-- | tipc/tipc.c | 130 |
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; +} |