Re: [Bridge] [RFC net-next Patch 0/3] net: bridge: mrp: Add support for Media Redundancy Protocol(MRP)
Hi Horatiu and Stephen, Horatiu, thanks for giving this a try. I am looking forward to maybe someday be able to run ERPS on white box switches. On 1/9/20 4:19 PM, Stephen Hemminger wrote: Can this be implemented in userspace? Putting STP in the kernel was a mistake (even original author says so). Adding more control protocols in kernel is a security and stability risk. Another case is VRRP, ERPS (ITU-T G.8032), VRRP group. My use-case might not be common, but I have machines with about 10k net_dev (QinQ), I would like to be able to do VRRP group on the outer VLANs, which are only a few hundred instances without excessive context switching. I would then keep the the normal keep-alive state machine in kernel, basically a BPF-based timed periodic packet emitter facility and a XDP recieve hook. So only setup and event handling has to context switched to user-space. Unfortunately I haven't had time to explore this yet, but I think such an approach could solve a few of the reasons that scalable bridge/ring/ha protocols have to wait 20 years before being implemented in Linux. -- Best regards Asbjørn Sloth Tønnesen Network Engineer Fiberby ApS - AS42541
Re: [Bridge] [RFC net-next Patch 0/3] net: bridge: mrp: Add support for Media Redundancy Protocol(MRP)
On Thu, 9 Jan 2020 16:06:37 +0100 Horatiu Vultur wrote: > Media Redundancy Protocol is a data network protocol standardized by > International Electrotechnical Commission as IEC 62439-2. It allows rings of > Ethernet switches to overcome any single failure with recovery time faster > than > STP. It is primarily used in Industrial Ethernet applications. > > This is the first proposal of implementing a subset of the standard. It > supports > only 2 roles of an MRP node. It supports only Media Redundancy Manager(MRM) > and > Media Redundancy Client(MRC). In a MRP ring, each node needs to support MRP > and > in a ring can be only one MRM and multiple MRC. It is possible to have > multiple > instances of MRP on a single node. But a port can be part of only one MRP > instance. > > The MRM is responsible for detecting when there is a loop in the ring. It is > sending the frame MRP_Test to detect the loops. It would send MRP_Test on both > ports in the ring and if the frame is received at the other end, then the ring > is closed. Meaning that there is a loop. In this case it sets the port state > to > BLOCKED, not allowing traffic to pass through except MRP frames. In case it > stops receiving MRP_Test frames from itself then the MRM will detect that the > ring is open, therefor it would notify the other nodes of this change and will > set the state of the port to be FORWARDING. > > The MRC is responsible for forwarding MRP_Test frames between the ring ports > (and not to flood on other ports) and to listen when there is a change in the > network to clear the FDB. > > Similar with STP, MRP is implemented on top of the bridge and they can't be > enable at the same time. While STP runs on all ports of the bridge, MRP needs > to > run only on 2 ports. > > The bridge needs to: > - notify when the link of one of the ports goes down or up, because MRP > instance > needs to react to link changes by sending MRP_LinkChange frames. > - notify when one of the ports are removed from the bridge or when the bridge > is destroyed, because if the port is part of the MRP ring then MRP state > machine should be stopped. > - add a handler to allow MRP instance to process MRP frames, if MRP is > enabled. > This is similar with STP design. > - add logic for MRP frames inside the bridge. The bridge will just detect MRP > frames and it would forward them to the upper layer to allow to process it. > - update the logic to update non-MRP frames. If MRP is enabled, then look also > at the state of the port to decide to forward or not. > > 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 > > If this patch series is well received, the in the future it could be extended > with the following: > - add support for Media Redundancy Automanager. This role allows a node to > detect if needs to behave as a MRM or MRC. The advantage of this role is > that > the user doesn't need to configure the nodes each time they are > added/removed > from a ring and it adds redundancy to the manager. > - add support for Interconnect rings. This allow to connect multiple rings. > - add HW offloading. The standard defines 4 recovery times (500, 200, 30 and > 10 > ms). To be able to achieve 30 and 10 it is required by the HW to generate > the > MRP_Test frames and detect when the ring is open/closed. > > Horatiu Vultur (3): > net: bridge: mrp: Add support for Media Redundancy Protocol > net: bridge: mrp: Integrate MRP into the bridge > net: bridge: mrp: Add netlink support to configure MRP > > include/uapi/linux/if_bridge.h | 27 + > include/uapi/linux/if_ether.h |1 + > include/uapi/linux/rtnetlink.h |7 + > net/bridge/Kconfig | 12 + > net/bridge/Makefile|2 + > net/bridge/br.c| 19 + > net/bridge/br_device.c |3 + > net/bridge/br_forward.c|1 + > net/bridge/br_if.c | 10 + > net/bridge/br_input.c | 22 + > net/bridge/br_mrp.c| 1517 > net/bridge/br_mrp_timer.c | 227 + > net/bridge/br_netlink.c|9 + > net/bridge/br_private.h| 30 + > net/bridge/br_private_mrp.h| 208 + >
[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 --- 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 ..8f6df19a --- /dev/null +++ b/bridge/mrp.c @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Get mrp table with netlink + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 { +
[Bridge] [RFC net-next Patch 1/3] net: bridge: mrp: Add support for Media Redundancy Protocol
This patch implements the core MRP state-machines and generation/parsing of the frames. The implementation is on top of the bridge. All MRP frames are received by the function 'br_mrp_recv' which adds the frame in a queue to process them. For each frame it needs to decide if the frame needs to be dropped, process or forward. And these decisions are taken by the functions: 'br_mrp_should_drop', 'br_mrp_should_process' and 'br_mrp_should_forward'. Currently there only 3 types for MRP that are supported: MRP_Test, MRP_Topo and MRP_LinkChange. - MRP_Test are generated by MRM to detect if the ring is open or closed. - MRP_Topo are generated by MRM to notify the network that is changed. - MRP_LinkChange are generated by MRC to notify MRM that the node lost connectivity on one of the ports. All these frames need to be send out multiple times at different intervals. To do that, there is a special workqueue, where all this work is added. This is implemented in the file 'br_mr_timer.c'. Each role has it's own state machine: - Media Redundancy Manager(MRM) can be in one of the states: AC_STAT1, PRM_UP, CHK_RO, CHK_RC. This one is responsible to send MRP_Test and MRP_Topo on both of the ring ports. It also needs to process the MRP_Test, and in case it receives one it means that the ring is closed and it would change the state to CHK_RC. Whenever it detects that the ring is open(didn't receive MRP_Test frames in a configured interval), it would send MRP_Topo frames on both of the ring ports to notify other nodes in the ring that the topology of the network is different. MRM needs to process MRP_LinkChange because these frames indicate a change in the topology. If the MRM is in the state CHK_RC, then it would block one of the ports, not allowing traffic to be flow except MRP frames and frames specified in IEEE 802.1D-2004 Table 7-10. - Media Redundancy Client(MRC) can be in one of the states: AC_STAT1, DE_IDLE, PT, DE, PT_IDLE. MRC is responsible to send MRP_LinkChange when one of the ring ports lost the connectivity. It needs to process MRP_Topo frames, this frame contains a field which indicates the time in which it needs to clear the FDB. MRC will need to forward all the MRP frames. In all the states the MRC will set the port in forwarding state, except when the port is down. The standards supports multiple recovery times. There are 4 recovery: 500ms, 200ms, 30ms, 10ms. The implementation adds support for all of them, default(500ms), but it looks hard to achieve the result 30ms or 10ms without hardware support. To decide if a non-MRP frame can be send to the other ring port the function 'should_deliver' is extended to check also the function 'br_mrp_allow_egress'. Question: the function 'br_mrp_allow_egress' is looking at the MRP state of the port which is a pointer. But this could be a race condition, because while this function is called the port can be removed, because the function 'br_mrp_allow_egres' is not protected by rtnl_lock. It would be overkill to take this lock for each frame. What is the correct solution here? Should I make the mrp_port a RCU pointer? In case the MRP is not enable then MRP frames and non-MRP frames are forward as before. Signed-off-by: Horatiu Vultur --- include/uapi/linux/if_ether.h |1 + net/bridge/Kconfig| 12 + net/bridge/Makefile |2 + net/bridge/br_mrp.c | 1236 + net/bridge/br_mrp_timer.c | 227 ++ net/bridge/br_private_mrp.h | 199 ++ 6 files changed, 1677 insertions(+) create mode 100644 net/bridge/br_mrp.c create mode 100644 net/bridge/br_mrp_timer.c create mode 100644 net/bridge/br_private_mrp.h diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index f6ceb2e63d1e..fb8a140b3bba 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -92,6 +92,7 @@ #define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ #define ETH_P_TIPC 0x88CA /* TIPC */ #define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ +#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */ #define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ #define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index e4fb050e2078..d07e3901aff6 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -61,3 +61,15 @@ config BRIDGE_VLAN_FILTERING Say N to exclude this support and reduce the binary size. If unsure, say Y. + +config BRIDGE_MRP + bool "MRP protocol" + depends on BRIDGE + default n + help + If you say Y here, then the Ethernet bridge will be able to run MRP + protocol to detect loops. + + Say N to exclude this support and
[Bridge] [RFC net-next Patch 0/3] net: bridge: mrp: Add support for Media Redundancy Protocol(MRP)
Media Redundancy Protocol is a data network protocol standardized by International Electrotechnical Commission as IEC 62439-2. It allows rings of Ethernet switches to overcome any single failure with recovery time faster than STP. It is primarily used in Industrial Ethernet applications. This is the first proposal of implementing a subset of the standard. It supports only 2 roles of an MRP node. It supports only Media Redundancy Manager(MRM) and Media Redundancy Client(MRC). In a MRP ring, each node needs to support MRP and in a ring can be only one MRM and multiple MRC. It is possible to have multiple instances of MRP on a single node. But a port can be part of only one MRP instance. The MRM is responsible for detecting when there is a loop in the ring. It is sending the frame MRP_Test to detect the loops. It would send MRP_Test on both ports in the ring and if the frame is received at the other end, then the ring is closed. Meaning that there is a loop. In this case it sets the port state to BLOCKED, not allowing traffic to pass through except MRP frames. In case it stops receiving MRP_Test frames from itself then the MRM will detect that the ring is open, therefor it would notify the other nodes of this change and will set the state of the port to be FORWARDING. The MRC is responsible for forwarding MRP_Test frames between the ring ports (and not to flood on other ports) and to listen when there is a change in the network to clear the FDB. Similar with STP, MRP is implemented on top of the bridge and they can't be enable at the same time. While STP runs on all ports of the bridge, MRP needs to run only on 2 ports. The bridge needs to: - notify when the link of one of the ports goes down or up, because MRP instance needs to react to link changes by sending MRP_LinkChange frames. - notify when one of the ports are removed from the bridge or when the bridge is destroyed, because if the port is part of the MRP ring then MRP state machine should be stopped. - add a handler to allow MRP instance to process MRP frames, if MRP is enabled. This is similar with STP design. - add logic for MRP frames inside the bridge. The bridge will just detect MRP frames and it would forward them to the upper layer to allow to process it. - update the logic to update non-MRP frames. If MRP is enabled, then look also at the state of the port to decide to forward or not. 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 If this patch series is well received, the in the future it could be extended with the following: - add support for Media Redundancy Automanager. This role allows a node to detect if needs to behave as a MRM or MRC. The advantage of this role is that the user doesn't need to configure the nodes each time they are added/removed from a ring and it adds redundancy to the manager. - add support for Interconnect rings. This allow to connect multiple rings. - add HW offloading. The standard defines 4 recovery times (500, 200, 30 and 10 ms). To be able to achieve 30 and 10 it is required by the HW to generate the MRP_Test frames and detect when the ring is open/closed. Horatiu Vultur (3): net: bridge: mrp: Add support for Media Redundancy Protocol net: bridge: mrp: Integrate MRP into the bridge net: bridge: mrp: Add netlink support to configure MRP include/uapi/linux/if_bridge.h | 27 + include/uapi/linux/if_ether.h |1 + include/uapi/linux/rtnetlink.h |7 + net/bridge/Kconfig | 12 + net/bridge/Makefile|2 + net/bridge/br.c| 19 + net/bridge/br_device.c |3 + net/bridge/br_forward.c|1 + net/bridge/br_if.c | 10 + net/bridge/br_input.c | 22 + net/bridge/br_mrp.c| 1517 net/bridge/br_mrp_timer.c | 227 + net/bridge/br_netlink.c|9 + net/bridge/br_private.h| 30 + net/bridge/br_private_mrp.h| 208 + security/selinux/nlmsgtab.c|5 +- 16 files changed, 2099 insertions(+), 1 deletion(-) create mode 100644 net/bridge/br_mrp.c create mode 100644 net/bridge/br_mrp_timer.c create mode 100644 net/bridge/br_private_mrp.h -- 2.17.1
[Bridge] [RFC net-next Patch 3/3] net: bridge: mrp: Add netlink support 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 --- include/uapi/linux/if_bridge.h | 27 include/uapi/linux/rtnetlink.h | 7 + net/bridge/br_mrp.c| 281 + net/bridge/br_netlink.c| 9 ++ net/bridge/br_private.h| 2 + net/bridge/br_private_mrp.h| 9 ++ security/selinux/nlmsgtab.c| 5 +- 7 files changed, 339 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index 4a58e3d7de46..00f4f465d62a 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -265,6 +265,33 @@ enum { }; #define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1) +#ifdef CONFIG_BRIDGE_MRP +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) +#endif + /* 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 1418a8362bb7..b1d72a5309cd 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -171,6 +171,13 @@ enum { RTM_GETLINKPROP, #define RTM_GETLINKPROPRTM_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/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index a84aab3f7114..4173021d3bfa 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -1234,3 +1234,284 @@ void br_mrp_port_uninit(struct net_bridge_port *port) mutex_unlock(>lock); } + +/* Do sanity checks and obtain device and the ring */ +static int br_mrp_parse(struct sk_buff *skb, struct nlmsghdr *nlh, + struct net_device **pdev, struct br_mrp_config *conf) +{ + struct nlattr *tb[MRP_ATTR_MAX + 1]; + struct net *net = sock_net(skb->sk); + struct br_port_msg *bpm; + struct net_device *dev; + int err; + + err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb, +MRP_ATTR_MAX, NULL, NULL); + if (err < 0) + return err; + + bpm = nlmsg_data(nlh); + if (bpm->ifindex == 0) { + pr_info("PF_BRIDGE: %s with invalid ifindex\n", __func__); + return -EINVAL; + } + + dev = __dev_get_by_index(net, bpm->ifindex); + if (!dev) { + pr_info("PF_BRIDGE: %s with unknown ifindex\n", __func__); + return -ENODEV; + } + + if (!(dev->priv_flags & IFF_EBRIDGE)) { + pr_info("PF_BRIDGE: %s with non-bridge\n", __func__); + return -EOPNOTSUPP; + } + + *pdev = dev; + + if (tb[MRP_ATTR_P_IFINDEX]) + conf->p_ifindex = nla_get_u32(tb[MRP_ATTR_P_IFINDEX]); + if (tb[MRP_ATTR_S_IFINDEX]) + conf->s_ifindex = nla_get_u32(tb[MRP_ATTR_S_IFINDEX]); + if (tb[MRP_ATTR_RING_ROLE]) + conf->ring_role =
[Bridge] [RFC net-next Patch 2/3] net: bridge: mrp: Integrate MRP into the bridge
To integrate MRP into the bridge, the bridge needs to do the following: - notify when the link of one of the ports goes down or up, because MRP instance needs to react to link changes by sending MRP frames. - notify when one of the ports are removed from the bridge or when the bridge is destroyed, because if the port is part of the MRP ring then MRP state machine should be stopped. - add a handler to allow MRP instance to process MRP frames, if MRP is enabled. This is similar with STP design. - add logic for MRP frames inside the bridge. The bridge will just detect MRP frames and it would forward them to the upper layer to allow to process it. - update the logic to update non-MRP frames. If MRP is enabled, then look at the state of the port to decide to forward or not. Signed-off-by: Horatiu Vultur --- net/bridge/br.c | 19 +++ net/bridge/br_device.c | 3 +++ net/bridge/br_forward.c | 1 + net/bridge/br_if.c | 10 ++ net/bridge/br_input.c | 22 ++ net/bridge/br_private.h | 28 6 files changed, 83 insertions(+) diff --git a/net/bridge/br.c b/net/bridge/br.c index b6fe30e3768f..9053378ca1e4 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -94,6 +94,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v spin_lock_bh(>lock); if (br->dev->flags & IFF_UP) { br_stp_disable_port(p); + br_mrp_port_link_change(p, false); notified = true; } spin_unlock_bh(>lock); @@ -103,6 +104,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v if (netif_running(br->dev) && netif_oper_up(dev)) { spin_lock_bh(>lock); br_stp_enable_port(p); + br_mrp_port_link_change(p, true); notified = true; spin_unlock_bh(>lock); } @@ -308,6 +310,13 @@ static const struct stp_proto br_stp_proto = { .rcv= br_stp_rcv, }; +#if IS_ENABLED(CONFIG_BRIDGE_MRP) +static struct packet_type mrp_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_MRP), + .func = br_mrp_recv, +}; +#endif + static int __init br_init(void) { int err; @@ -320,6 +329,13 @@ static int __init br_init(void) return err; } +#if IS_ENABLED(CONFIG_BRIDGE_MRP) + /* Allow all MRP frames to be processed by the upper layer. The MRP +* frames can be dropped or forward on other MRP ports +*/ + dev_add_pack(_packet_type); +#endif + err = br_fdb_init(); if (err) goto err_out; @@ -376,6 +392,9 @@ static int __init br_init(void) static void __exit br_deinit(void) { stp_proto_unregister(_stp_proto); +#if IS_ENABLED(CONFIG_BRIDGE_MRP) + dev_remove_pack(_packet_type); +#endif br_netlink_fini(); unregister_switchdev_notifier(_switchdev_notifier); unregister_netdevice_notifier(_device_notifier); diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index fb38add21b37..29966754d86a 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -464,6 +464,9 @@ void br_dev_setup(struct net_device *dev) spin_lock_init(>lock); INIT_LIST_HEAD(>port_list); INIT_HLIST_HEAD(>fdb_list); +#ifdef CONFIG_BRIDGE_MRP + INIT_LIST_HEAD(>mrp_list); +#endif spin_lock_init(>hash_lock); br->bridge_id.prio[0] = 0x80; diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 86637000f275..306425bc9899 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -27,6 +27,7 @@ static inline int should_deliver(const struct net_bridge_port *p, return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING && nbp_switchdev_allowed_egress(p, skb) && + br_mrp_allow_egress(p, skb) && !br_skb_isolated(p, skb); } diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 4fe30b182ee7..bf7a467b5f33 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -80,11 +80,13 @@ void br_port_carrier_check(struct net_bridge_port *p, bool *notified) br_stp_enable_port(p); *notified = true; } + br_mrp_port_link_change(p, true); } else { if (p->state != BR_STATE_DISABLED) { br_stp_disable_port(p); *notified = true; } + br_mrp_port_link_change(p, false); } spin_unlock_bh(>lock); } @@ -331,6 +333,9 @@ static void del_nbp(struct net_bridge_port *p) spin_lock_bh(>lock); br_stp_disable_port(p); +#ifdef