[Bridge] [RFC iproute2] bridge: Add suppport to configure MRP

Horatiu Vultur horatiu.vultur at microchip.com
Thu Jan 9 15:31:32 UTC 2020


Extend br_netlink to be able to create/delete MRP instances. The current
configurations options for each instance are:
- set primary port
- set secondary port
- set MRP ring role (MRM or MRC)
- set MRP ring id.

To create a MRP instance on the bridge:
$ bridge mrp add dev br0 p_port eth0 s_port eth1 ring_role 2 ring_id 1

Where:
p_port, s_port: can be any port under the bridge
ring_role: can have the value 1(MRC - Media Redundancy Client) or
           2(MRM - Media Redundancy Manager). In a ring can be only one MRM.
ring_id: unique id for each MRP instance.

It is possible to create multiple instances. Each instance has to have it's own
ring_id and a port can't be part of multiple instances:
$ bridge mrp add dev br0 p_port eth2 s_port eth3 ring_role 1 ring_id 2

To see current MRP instances and their status:
$ bridge mrp show
dev br0 p_port eth2 s_port eth3 ring_role 1 ring_id 2 ring_state 3
dev br0 p_port eth0 s_port eth1 ring_role 2 ring_id 1 ring_state 4

Where:
p_port, s_port, ring_role, ring_id: represent the configuration values. It is
   possible for primary port to change the role with the secondary port.
   It depends on the states through which the node goes.
ring_state: depends on the ring_role. If mrp_ring_role is 1(MRC) then the values
   of mrp_ring_state can be: 0(AC_STAT1), 1(DE_IDLE), 2(PT), 3(DE), 4(PT_IDLE).
   If mrp_ring_role is 2(MRM) then the values of mrp_ring_state can be:
   0(AC_STAT1), 1(PRM_UP), 2(CHK_RO), 3(CHK_RC).

Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com>
---
 bridge/Makefile                |   2 +-
 bridge/br_common.h             |   1 +
 bridge/bridge.c                |   3 +-
 bridge/mrp.c                   | 252 +++++++++++++++++++++++++++++++++
 include/libnetlink.h           |   2 +
 include/uapi/linux/if_bridge.h |  25 ++++
 include/uapi/linux/rtnetlink.h |   7 +
 lib/libnetlink.c               |  16 +++
 8 files changed, 306 insertions(+), 2 deletions(-)
 create mode 100644 bridge/mrp.c

diff --git a/bridge/Makefile b/bridge/Makefile
index c6b7d08d..330b5a8c 100644
--- a/bridge/Makefile
+++ b/bridge/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o
+BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o mrp.o
 
 include ../config.mk
 
diff --git a/bridge/br_common.h b/bridge/br_common.h
index b5798da3..b2639d18 100644
--- a/bridge/br_common.h
+++ b/bridge/br_common.h
@@ -13,6 +13,7 @@ int print_fdb(struct nlmsghdr *n, void *arg);
 
 int do_fdb(int argc, char **argv);
 int do_mdb(int argc, char **argv);
+int do_mrp(int argc, char **argv);
 int do_monitor(int argc, char **argv);
 int do_vlan(int argc, char **argv);
 int do_link(int argc, char **argv);
diff --git a/bridge/bridge.c b/bridge/bridge.c
index a50d9d59..ebbee013 100644
--- a/bridge/bridge.c
+++ b/bridge/bridge.c
@@ -37,7 +37,7 @@ static void usage(void)
 	fprintf(stderr,
 "Usage: bridge [ OPTIONS ] OBJECT { COMMAND | help }\n"
 "       bridge [ -force ] -batch filename\n"
-"where	OBJECT := { link | fdb | mdb | vlan | monitor }\n"
+"where	OBJECT := { link | fdb | mdb | mrp | vlan | monitor }\n"
 "	OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] |\n"
 "		     -o[neline] | -t[imestamp] | -n[etns] name |\n"
 "		     -c[ompressvlans] -color -p[retty] -j[son] }\n");
@@ -57,6 +57,7 @@ static const struct cmd {
 	{ "link",	do_link },
 	{ "fdb",	do_fdb },
 	{ "mdb",	do_mdb },
+	{ "mrp",	do_mrp },
 	{ "vlan",	do_vlan },
 	{ "monitor",	do_monitor },
 	{ "help",	do_help },
diff --git a/bridge/mrp.c b/bridge/mrp.c
new file mode 100644
index 00000000..8f6df19a
--- /dev/null
+++ b/bridge/mrp.c
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Get mrp table with netlink
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+#include <linux/if_ether.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "libnetlink.h"
+#include "br_common.h"
+#include "rt_names.h"
+#include "utils.h"
+#include "json_print.h"
+
+#ifndef MRPA_RTA
+#define MRPA_RTA(r) \
+	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
+#endif
+
+static void usage(void)
+{
+	fprintf(stderr,
+		"Usage: bridge mrp { add | del } dev DEV p_port PORT s_port PORT ring_role ROLE ring_nr ID\n"
+		"       bridge mpr {show}\n");
+	exit(-1);
+}
+
+static void print_mrp_entry(FILE *f, int ifindex,
+			    struct nlmsghdr *n, struct rtattr **tb)
+{
+	const char *dev;
+
+	open_json_object(NULL);
+
+	dev = ll_index_to_name(ifindex);
+	print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev);
+
+	if (tb[MRP_ATTR_P_IFINDEX]) {
+		dev = ll_index_to_name(rta_getattr_u32(tb[MRP_ATTR_P_IFINDEX]));
+		print_color_string(PRINT_ANY, COLOR_IFNAME, "p_port",
+				   " p_port %s", dev);
+	} else {
+		print_color_string(PRINT_ANY, COLOR_IFNAME, "p_port",
+				   " p_port %s", "*");
+	}
+
+	if (tb[MRP_ATTR_S_IFINDEX]) {
+		dev = ll_index_to_name(rta_getattr_u32(tb[MRP_ATTR_S_IFINDEX]));
+		print_color_string(PRINT_ANY, COLOR_IFNAME, "s_port",
+				   " s_port %s", dev);
+	} else {
+		print_color_string(PRINT_ANY, COLOR_IFNAME, "s_port",
+				   " s_port %s", "*");
+	}
+
+	if (tb[MRP_ATTR_RING_NR])
+		print_uint(PRINT_ANY, "ring_id", " ring_id %u",
+			   rta_getattr_u32(tb[MRP_ATTR_RING_NR]));
+	if (tb[MRP_ATTR_RING_ROLE])
+		print_uint(PRINT_ANY, "ring_role", " ring_role %u",
+			   rta_getattr_u32(tb[MRP_ATTR_RING_ROLE]));
+	if (tb[MRP_ATTR_RING_STATE])
+		print_uint(PRINT_ANY, "ring_state", " ring_state %u",
+			   rta_getattr_u32(tb[MRP_ATTR_RING_STATE]));
+
+	print_nl();
+	close_json_object();
+}
+
+static void print_mrp_entries(FILE *fp, struct nlmsghdr *n,
+			      int ifindex,  struct rtattr *attr)
+{
+	struct rtattr *etb[MRP_ATTR_MAX + 1];
+	int rem = RTA_PAYLOAD(attr);
+	struct rtattr *i;
+
+	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+		parse_rtattr(etb, MRP_ATTR_MAX, RTA_DATA(i), rem - 4);
+		print_mrp_entry(fp, ifindex, n, etb);
+	}
+}
+
+static int __parse_mrp_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
+{
+	struct br_port_msg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+
+	if (n->nlmsg_type != RTM_GETMRP &&
+	    n->nlmsg_type != RTM_NEWMRP &&
+	    n->nlmsg_type != RTM_DELMRP) {
+		fprintf(stderr,
+			"Not RTM_GETMRP, RTM_NEWMRP or RTM_DELMRP: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+
+		return 0;
+	}
+
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	parse_rtattr(tb, MRPA_MAX, MRPA_RTA(r),
+		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+	return 1;
+}
+
+static int print_mrps(struct nlmsghdr *n, void *arg)
+{
+	struct br_port_msg *r = NLMSG_DATA(n);
+	struct rtattr *tb[MRPA_MAX+1];
+	FILE *fp = arg;
+	int ret;
+
+	ret = __parse_mrp_nlmsg(n, tb);
+	if (ret != 1)
+		return ret;
+
+	if (tb[MRPA_MRP])
+		print_mrp_entries(fp, n, r->ifindex, tb[MRPA_MRP]);
+
+	return 0;
+}
+
+static int mrp_show(int argc, char **argv)
+{
+	new_json_obj(json);
+	open_json_object(NULL);
+
+	/* get mrp entries */
+	if (rtnl_mrpdump_req(&rth, PF_BRIDGE) < 0) {
+		perror("Cannot send dump request");
+		return -1;
+	}
+
+	open_json_array(PRINT_JSON, "mrp");
+	if (rtnl_dump_filter(&rth, print_mrps, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		return -1;
+	}
+	close_json_array(PRINT_JSON, NULL);
+
+	close_json_object();
+	delete_json_obj();
+	fflush(stdout);
+
+	return 0;
+	return 0;
+}
+
+static int mrp_modify(int cmd, int flags, int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr	n;
+		struct br_port_msg	bpm;
+		char			buf[1024];
+	} req = {
+		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
+		.n.nlmsg_flags = NLM_F_REQUEST | flags,
+		.n.nlmsg_type = cmd,
+		.bpm.family = PF_BRIDGE,
+	};
+	char *dev = NULL, *p_port = NULL, *s_port = NULL;
+	uint8_t ring_role = 0;
+	uint32_t ring_id = 0, p_ifindex = 0, s_ifindex = 0;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			dev = *argv;
+		} else if (strcmp(*argv, "p_port") == 0) {
+			NEXT_ARG();
+			p_port = *argv;
+		} else if (strcmp(*argv, "s_port") == 0) {
+			NEXT_ARG();
+			s_port = *argv;
+		} else if (strcmp(*argv, "ring_role") == 0) {
+			NEXT_ARG();
+			ring_role = atoi(*argv);
+		} else if (strcmp(*argv, "ring_id") == 0) {
+			NEXT_ARG();
+			ring_id = atoi(*argv);
+		} else {
+			if (matches(*argv, "help") == 0)
+				usage();
+		}
+		argc--; argv++;
+	}
+
+	if (cmd == RTM_DELMRP && (dev == NULL || ring_id == 0)) {
+		fprintf(stderr, "Device and ring_id are required arguments for del. \n");
+		return -1;
+	}
+	if (cmd == RTM_NEWMRP &&
+	    (dev == NULL || p_port == NULL || s_port == NULL || ring_role == 0 || ring_id == 0)) {
+		fprintf(stderr, "Device, p_port, s_port, ring_role and ring_id are required arguments for add.\n");
+		return -1;
+	}
+
+	req.bpm.ifindex = ll_name_to_index(dev);
+	if (!req.bpm.ifindex)
+		return nodev(dev);
+
+	p_ifindex = ll_name_to_index(p_port);
+	if (!p_ifindex && cmd == RTM_NEWMRP)
+		return nodev(p_port);
+
+	s_ifindex = ll_name_to_index(s_port);
+	if (!s_ifindex && cmd == RTM_NEWMRP)
+		return nodev(p_port);
+
+	addattr32(&req.n, sizeof(req), MRP_ATTR_P_IFINDEX, p_ifindex);
+	addattr32(&req.n, sizeof(req), MRP_ATTR_S_IFINDEX, s_ifindex);
+	addattr8(&req.n, sizeof(req), MRP_ATTR_RING_ROLE, ring_role);
+	addattr32(&req.n, sizeof(req), MRP_ATTR_RING_NR, ring_id);
+
+	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+		return -1;
+
+	return 0;
+}
+
+int do_mrp(int argc, char **argv)
+{
+	ll_init_map(&rth);
+
+	if (argc > 0) {
+		if (matches(*argv, "add") == 0)
+			return mrp_modify(RTM_NEWMRP, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
+		if (matches(*argv, "delete") == 0)
+			return mrp_modify(RTM_DELMRP, 0, argc-1, argv+1);
+		if (matches(*argv, "show") == 0)
+			return mrp_show(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return mrp_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mrp help\".\n", *argv);
+	exit(-1);
+}
diff --git a/include/libnetlink.h b/include/libnetlink.h
index 8ebdc6d3..4e065164 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -69,6 +69,8 @@ int rtnl_neightbldump_req(struct rtnl_handle *rth, int family)
 	__attribute__((warn_unused_result));
 int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
 	__attribute__((warn_unused_result));
+int rtnl_mrpdump_req(struct rtnl_handle *rth, int family)
+	__attribute__((warn_unused_result));
 int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
 	__attribute__((warn_unused_result));
 
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 31fc51bd..0220fe5f 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -256,6 +256,31 @@ enum {
 };
 #define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
 
+enum {
+	MRPA_UNSPEC,
+	MRPA_MRP,
+	__MRPA_MAX,
+};
+#define MRPA_MAX (__MRPA_MAX - 1)
+
+enum {
+	MRPA_MRP_UNSPEC,
+	MRPA_MRP_ENTRY,
+	__MRPA_MRP_MAX,
+};
+#define MRPA_MRP_MAX (__MRPA_MRP_MAX - 1)
+
+enum {
+	MRP_ATTR_UNSPEC,
+	MRP_ATTR_P_IFINDEX,
+	MRP_ATTR_S_IFINDEX,
+	MRP_ATTR_RING_ROLE,
+	MRP_ATTR_RING_NR,
+	MRP_ATTR_RING_STATE,
+	__MRP_ATTR_MAX,
+};
+#define MRP_ATTR_MAX (__MRP_ATTR_MAX - 1)
+
 /* Embedded inside LINK_XSTATS_TYPE_BRIDGE */
 enum {
 	BRIDGE_XSTATS_UNSPEC,
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 4b93791c..8335360f 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -171,6 +171,13 @@ enum {
 	RTM_GETLINKPROP,
 #define RTM_GETLINKPROP	RTM_GETLINKPROP
 
+	RTM_NEWMRP = 112,
+#define RTM_NEWMRP	RTM_NEWMRP
+	RTM_DELMRP,
+#define RTM_DELMRP	RTM_DELMRP
+	RTM_GETMRP,
+#define RTM_GETMRP	RTM_GETMRP
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index e02d6294..f1f84733 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -421,6 +421,22 @@ int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
 	return send(rth->fd, &req, sizeof(req), 0);
 }
 
+int rtnl_mrpdump_req(struct rtnl_handle *rth, int family)
+{
+	struct {
+		struct nlmsghdr nlh;
+		struct br_port_msg bpm;
+	} req = {
+		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
+		.nlh.nlmsg_type = RTM_GETMRP,
+		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
+		.bpm.family = family,
+	};
+
+	return send(rth->fd, &req, sizeof(req), 0);
+}
+
 int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
 {
 	struct {
-- 
2.17.1



More information about the Bridge mailing list