From 9df1500bde8e609dcbbecbefa0eb5a29d9e6f7f5 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Fri, 27 Aug 2021 16:52:34 +0200
Subject: [PATCH 14/27] kernel: add bdpu filter support

Signed-off-by: John Crispin <john@phrozen.org>
---
 ...l-knob-for-filtering-rx-tx-BPDU-pack.patch | 107 ++++++++++++++++++
 ...l-knob-for-filtering-rx-tx-BPDU-pack.patch | 107 ++++++++++++++++++
 2 files changed, 214 insertions(+)
 create mode 100644 target/linux/generic/pending-5.10/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch
 create mode 100644 target/linux/generic/pending-5.4/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch

diff --git a/target/linux/generic/pending-5.10/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch b/target/linux/generic/pending-5.10/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch
new file mode 100644
index 0000000000..918ae05d12
--- /dev/null
+++ b/target/linux/generic/pending-5.10/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch
@@ -0,0 +1,107 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 27 Aug 2021 12:22:32 +0200
+Subject: [PATCH] bridge: add sysctl knob for filtering rx/tx BPDU packets on a
+ port
+
+Some devices (e.g. wireless APs) can't have devices behind them be part of
+a bridge topology with redundant links, due to address limitations.
+Additionally, broadcast traffic on these devices is somewhat expensive, due to
+the low data rate and wakeups of clients in powersave mode.
+This sysctl knob can be used to ensure that BPDU packets are never sent
+or forwarded to/from these devices
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -56,6 +56,7 @@ struct br_ip_list {
+ #define BR_MRP_AWARE		BIT(17)
+ #define BR_MRP_LOST_CONT	BIT(18)
+ #define BR_MRP_LOST_IN_CONT	BIT(19)
++#define BR_BPDU_FILTER		BIT(20)
+ 
+ #define BR_DEFAULT_AGEING_TIME	(300 * HZ)
+ 
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -191,6 +191,7 @@ out:
+ void br_flood(struct net_bridge *br, struct sk_buff *skb,
+ 	      enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
+ {
++	const unsigned char *dest = eth_hdr(skb)->h_dest;
+ 	struct net_bridge_port *prev = NULL;
+ 	struct net_bridge_port *p;
+ 
+@@ -206,6 +207,10 @@ void br_flood(struct net_bridge *br, str
+ 		case BR_PKT_MULTICAST:
+ 			if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
+ 				continue;
++			if ((p->flags & BR_BPDU_FILTER) &&
++			    unlikely(is_link_local_ether_addr(dest) &&
++				     dest[5] == 0))
++				continue;
+ 			break;
+ 		case BR_PKT_BROADCAST:
+ 			if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -305,6 +305,8 @@ static rx_handler_result_t br_handle_fra
+ 		fwd_mask |= p->group_fwd_mask;
+ 		switch (dest[5]) {
+ 		case 0x00:	/* Bridge Group Address */
++			if (p->flags & BR_BPDU_FILTER)
++				goto drop;
+ 			/* If STP is turned off,
+ 			   then must forward to keep loop detection */
+ 			if (p->br->stp_enabled == BR_NO_STP ||
+--- a/net/bridge/br_sysfs_if.c
++++ b/net/bridge/br_sysfs_if.c
+@@ -233,6 +233,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA
+ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
+ BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
+ BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
++BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
+ 
+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
+@@ -285,6 +286,7 @@ static const struct brport_attribute *br
+ 	&brport_attr_group_fwd_mask,
+ 	&brport_attr_neigh_suppress,
+ 	&brport_attr_isolated,
++	&brport_attr_bpdu_filter,
+ 	&brport_attr_backup_port,
+ 	NULL
+ };
+--- a/net/bridge/br_stp_bpdu.c
++++ b/net/bridge/br_stp_bpdu.c
+@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid
+ {
+ 	unsigned char buf[35];
+ 
+-	if (p->br->stp_enabled != BR_KERNEL_STP)
++	if (p->br->stp_enabled != BR_KERNEL_STP ||
++	    (p->flags & BR_BPDU_FILTER))
+ 		return;
+ 
+ 	buf[0] = 0;
+@@ -127,7 +128,8 @@ void br_send_tcn_bpdu(struct net_bridge_
+ {
+ 	unsigned char buf[4];
+ 
+-	if (p->br->stp_enabled != BR_KERNEL_STP)
++	if (p->br->stp_enabled != BR_KERNEL_STP ||
++	    (p->flags & BR_BPDU_FILTER))
+ 		return;
+ 
+ 	buf[0] = 0;
+@@ -172,6 +174,9 @@ void br_stp_rcv(const struct stp_proto *
+ 	if (!(br->dev->flags & IFF_UP))
+ 		goto out;
+ 
++	if (p->flags & BR_BPDU_FILTER)
++		goto out;
++
+ 	if (p->state == BR_STATE_DISABLED)
+ 		goto out;
+ 
diff --git a/target/linux/generic/pending-5.4/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch b/target/linux/generic/pending-5.4/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch
new file mode 100644
index 0000000000..586d264cd5
--- /dev/null
+++ b/target/linux/generic/pending-5.4/710-bridge-add-sysctl-knob-for-filtering-rx-tx-BPDU-pack.patch
@@ -0,0 +1,107 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 27 Aug 2021 12:22:32 +0200
+Subject: [PATCH] bridge: add sysctl knob for filtering rx/tx BPDU packets on a
+ port
+
+Some devices (e.g. wireless APs) can't have devices behind them be part of
+a bridge topology with redundant links, due to address limitations.
+Additionally, broadcast traffic on these devices is somewhat expensive, due to
+the low data rate and wakeups of clients in powersave mode.
+This sysctl knob can be used to ensure that BPDU packets are never sent
+or forwarded to/from these devices
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -47,6 +47,7 @@ struct br_ip_list {
+ #define BR_BCAST_FLOOD		BIT(14)
+ #define BR_NEIGH_SUPPRESS	BIT(15)
+ #define BR_ISOLATED		BIT(16)
++#define BR_BPDU_FILTER		BIT(17)
+ 
+ #define BR_DEFAULT_AGEING_TIME	(300 * HZ)
+ 
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -191,6 +191,7 @@ out:
+ void br_flood(struct net_bridge *br, struct sk_buff *skb,
+ 	      enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
+ {
++	const unsigned char *dest = eth_hdr(skb)->h_dest;
+ 	struct net_bridge_port *prev = NULL;
+ 	struct net_bridge_port *p;
+ 
+@@ -206,6 +207,10 @@ void br_flood(struct net_bridge *br, str
+ 		case BR_PKT_MULTICAST:
+ 			if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
+ 				continue;
++			if ((p->flags & BR_BPDU_FILTER) &&
++			    unlikely(is_link_local_ether_addr(dest) &&
++				     dest[5] == 0))
++				continue;
+ 			break;
+ 		case BR_PKT_BROADCAST:
+ 			if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -300,6 +300,8 @@ rx_handler_result_t br_handle_frame(stru
+ 		fwd_mask |= p->group_fwd_mask;
+ 		switch (dest[5]) {
+ 		case 0x00:	/* Bridge Group Address */
++			if (p->flags & BR_BPDU_FILTER)
++				goto drop;
+ 			/* If STP is turned off,
+ 			   then must forward to keep loop detection */
+ 			if (p->br->stp_enabled == BR_NO_STP ||
+--- a/net/bridge/br_sysfs_if.c
++++ b/net/bridge/br_sysfs_if.c
+@@ -233,6 +233,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA
+ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
+ BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
+ BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
++BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
+ 
+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
+@@ -285,6 +286,7 @@ static const struct brport_attribute *br
+ 	&brport_attr_group_fwd_mask,
+ 	&brport_attr_neigh_suppress,
+ 	&brport_attr_isolated,
++	&brport_attr_bpdu_filter,
+ 	&brport_attr_backup_port,
+ 	NULL
+ };
+--- a/net/bridge/br_stp_bpdu.c
++++ b/net/bridge/br_stp_bpdu.c
+@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid
+ {
+ 	unsigned char buf[35];
+ 
+-	if (p->br->stp_enabled != BR_KERNEL_STP)
++	if (p->br->stp_enabled != BR_KERNEL_STP ||
++	    (p->flags & BR_BPDU_FILTER))
+ 		return;
+ 
+ 	buf[0] = 0;
+@@ -125,7 +126,8 @@ void br_send_tcn_bpdu(struct net_bridge_
+ {
+ 	unsigned char buf[4];
+ 
+-	if (p->br->stp_enabled != BR_KERNEL_STP)
++	if (p->br->stp_enabled != BR_KERNEL_STP ||
++	    (p->flags & BR_BPDU_FILTER))
+ 		return;
+ 
+ 	buf[0] = 0;
+@@ -168,6 +170,9 @@ void br_stp_rcv(const struct stp_proto *
+ 	if (!(br->dev->flags & IFF_UP))
+ 		goto out;
+ 
++	if (p->flags & BR_BPDU_FILTER)
++		goto out;
++
+ 	if (p->state == BR_STATE_DISABLED)
+ 		goto out;
+ 
-- 
2.25.1

