@@ -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
@@ -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);
@@ -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 },
new file mode 100644
@@ -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);
+}
@@ -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));
@@ -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,
@@ -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)
};
@@ -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 {
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@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