Horatiu Vultur
2020-Jan-09 15:31 UTC
[Bridge] [RFC iproute2] bridge: Add suppport to configure MRP
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