summaryrefslogtreecommitdiff
path: root/bridge/mst.c
diff options
context:
space:
mode:
Diffstat (limited to 'bridge/mst.c')
-rw-r--r--bridge/mst.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/bridge/mst.c b/bridge/mst.c
new file mode 100644
index 00000000..873ca536
--- /dev/null
+++ b/bridge/mst.c
@@ -0,0 +1,262 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Get/set Multiple Spanning Tree (MST) states
+ */
+
+#include <stdio.h>
+#include <linux/if_bridge.h>
+#include <net/if.h>
+
+#include "libnetlink.h"
+#include "json_print.h"
+#include "utils.h"
+
+#include "br_common.h"
+
+#define MST_ID_LEN 9
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
+static unsigned int filter_index;
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "Usage: bridge mst set dev DEV msti MSTI state STATE\n"
+ " bridge mst {show} [ dev DEV ]\n");
+ exit(-1);
+}
+
+static void print_mst_entry(struct rtattr *a, FILE *fp)
+{
+ struct rtattr *tb[IFLA_BRIDGE_MST_ENTRY_MAX + 1];
+ __u16 msti = 0;
+ __u8 state = 0;
+
+ parse_rtattr_flags(tb, IFLA_BRIDGE_MST_ENTRY_MAX, RTA_DATA(a),
+ RTA_PAYLOAD(a), NLA_F_NESTED);
+
+
+ if (!(tb[IFLA_BRIDGE_MST_ENTRY_MSTI] &&
+ tb[IFLA_BRIDGE_MST_ENTRY_STATE])) {
+ fprintf(stderr, "BUG: broken MST entry");
+ return;
+ }
+
+ msti = rta_getattr_u16(tb[IFLA_BRIDGE_MST_ENTRY_MSTI]);
+ state = rta_getattr_u8(tb[IFLA_BRIDGE_MST_ENTRY_STATE]);
+
+ open_json_object(NULL);
+ print_uint(PRINT_ANY, "msti", "%u", msti);
+ print_nl();
+ print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
+ print_stp_state(state);
+ print_nl();
+ close_json_object();
+}
+
+static int print_msts(struct nlmsghdr *n, void *arg)
+{
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr *af_spec, *mst, *a;
+ int rem = n->nlmsg_len;
+ bool opened = false;
+
+ rem -= NLMSG_LENGTH(sizeof(*ifi));
+ if (rem < 0) {
+ fprintf(stderr, "BUG: wrong nlmsg len %d\n", rem);
+ return -1;
+ }
+
+ af_spec = parse_rtattr_one(IFLA_AF_SPEC, IFLA_RTA(ifi), rem);
+ if (!af_spec)
+ return -1;
+
+ if (filter_index && filter_index != ifi->ifi_index)
+ return 0;
+
+ mst = parse_rtattr_one_nested(NLA_F_NESTED | IFLA_BRIDGE_MST, af_spec);
+ if (!mst)
+ return 0;
+
+ rem = RTA_PAYLOAD(mst);
+ for (a = RTA_DATA(mst); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
+ unsigned short rta_type = a->rta_type & NLA_TYPE_MASK;
+
+ if (rta_type > IFLA_BRIDGE_MST_MAX)
+ continue;
+
+ switch (rta_type) {
+ case IFLA_BRIDGE_MST_ENTRY:
+ if (!opened) {
+ open_json_object(NULL);
+ print_color_string(PRINT_ANY, COLOR_IFNAME,
+ "ifname",
+ "%-" __stringify(IFNAMSIZ) "s ",
+ ll_index_to_name(ifi->ifi_index));
+ open_json_array(PRINT_JSON, "mst");
+ opened = true;
+ } else {
+ print_string(PRINT_FP, NULL, "%-"
+ __stringify(IFNAMSIZ) "s ", "");
+ }
+
+ print_mst_entry(a, arg);
+ break;
+ }
+ }
+
+ if (opened) {
+ close_json_array(PRINT_JSON, NULL);
+ close_json_object();
+ }
+
+ return 0;
+}
+
+static int mst_show(int argc, char **argv)
+{
+ char *filter_dev = NULL;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ if (filter_dev)
+ duparg("dev", *argv);
+ filter_dev = *argv;
+ }
+ argc--; argv++;
+ }
+
+ if (filter_dev) {
+ filter_index = ll_name_to_index(filter_dev);
+ if (!filter_index)
+ return nodev(filter_dev);
+ }
+
+ if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE, RTEXT_FILTER_MST) < 0) {
+ perror("Cannon send dump request");
+ exit(1);
+ }
+
+ new_json_obj(json);
+
+ if (!is_json_context()) {
+ printf("%-" __stringify(IFNAMSIZ) "s "
+ "%-" __stringify(MST_ID_LEN) "s",
+ "port", "msti");
+ printf("\n");
+ }
+
+ if (rtnl_dump_filter(&rth, print_msts, stdout) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return -1;
+ }
+
+ delete_json_obj();
+ fflush(stdout);
+ return 0;
+}
+
+static int mst_set(int argc, char **argv)
+{
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg ifi;
+ char buf[512];
+ } req = {
+ .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .n.nlmsg_flags = NLM_F_REQUEST,
+ .n.nlmsg_type = RTM_SETLINK,
+ .ifi.ifi_family = PF_BRIDGE,
+ };
+ char *d = NULL, *m = NULL, *s = NULL, *endptr;
+ struct rtattr *af_spec, *mst, *entry;
+ __u16 msti;
+ __u8 state;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ d = *argv;
+ } else if (strcmp(*argv, "msti") == 0) {
+ NEXT_ARG();
+ m = *argv;
+ } else if (strcmp(*argv, "state") == 0) {
+ NEXT_ARG();
+ s = *argv;
+ } else {
+ if (matches(*argv, "help") == 0)
+ usage();
+ }
+ argc--; argv++;
+ }
+
+ if (d == NULL || m == NULL || s == NULL) {
+ fprintf(stderr, "Device, MSTI and state are required arguments.\n");
+ return -1;
+ }
+
+ req.ifi.ifi_index = ll_name_to_index(d);
+ if (!req.ifi.ifi_index)
+ return nodev(d);
+
+ msti = strtol(m, &endptr, 10);
+ if (!(*s != '\0' && *endptr == '\0')) {
+ fprintf(stderr,
+ "Error: invalid MSTI\n");
+ return -1;
+ }
+
+ state = strtol(s, &endptr, 10);
+ if (!(*s != '\0' && *endptr == '\0')) {
+ state = parse_stp_state(s);
+ if (state == -1) {
+ fprintf(stderr,
+ "Error: invalid STP port state\n");
+ return -1;
+ }
+ }
+
+ af_spec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
+ mst = addattr_nest(&req.n, sizeof(req), IFLA_BRIDGE_MST);
+
+ entry = addattr_nest(&req.n, sizeof(req), IFLA_BRIDGE_MST_ENTRY);
+ entry->rta_type |= NLA_F_NESTED;
+
+ addattr16(&req.n, sizeof(req), IFLA_BRIDGE_MST_ENTRY_MSTI, msti);
+ addattr8(&req.n, sizeof(req), IFLA_BRIDGE_MST_ENTRY_STATE, state);
+
+ addattr_nest_end(&req.n, entry);
+
+ addattr_nest_end(&req.n, mst);
+ addattr_nest_end(&req.n, af_spec);
+
+
+ if (rtnl_talk(&rth, &req.n, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+int do_mst(int argc, char **argv)
+{
+ ll_init_map(&rth);
+
+ if (argc > 0) {
+ if (matches(*argv, "set") == 0)
+ return mst_set(argc-1, argv+1);
+
+ if (matches(*argv, "show") == 0 ||
+ matches(*argv, "lst") == 0 ||
+ matches(*argv, "list") == 0)
+ return mst_show(argc-1, argv+1);
+ if (matches(*argv, "help") == 0)
+ usage();
+ } else
+ return mst_show(0, NULL);
+
+ fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mst help\".\n", *argv);
+ exit(-1);
+}