Re: [Bridge] [RFC net-next Patch 0/3] net: bridge: mrp: Add support for Media Redundancy Protocol(MRP)

2020-01-09 Thread Asbjørn Sloth Tønnesen via Bridge

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)

2020-01-09 Thread Stephen Hemminger
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

2020-01-09 Thread Horatiu Vultur
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

2020-01-09 Thread Horatiu Vultur
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)

2020-01-09 Thread Horatiu Vultur
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

2020-01-09 Thread Horatiu Vultur
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

2020-01-09 Thread Horatiu Vultur
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