summaryrefslogtreecommitdiff
path: root/net/bridge
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br_if.c32
-rw-r--r--net/bridge/br_input.c23
-rw-r--r--net/bridge/br_mrp_netlink.c2
-rw-r--r--net/bridge/br_netlink.c1
-rw-r--r--net/bridge/br_private.h13
-rw-r--r--net/bridge/br_stp_if.c2
6 files changed, 53 insertions, 20 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index ca685c0cdf95..a0e9a7937412 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -563,18 +563,32 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
unsigned br_hr, dev_hr;
bool changed_addr;
- /* Don't allow bridging non-ethernet like devices, or DSA-enabled
- * master network devices since the bridge layer rx_handler prevents
- * the DSA fake ethertype handler to be invoked, so we do not strip off
- * the DSA switch tag protocol header and the bridge layer just return
- * RX_HANDLER_CONSUMED, stopping RX processing for these frames.
- */
+ /* Don't allow bridging non-ethernet like devices. */
if ((dev->flags & IFF_LOOPBACK) ||
dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
- !is_valid_ether_addr(dev->dev_addr) ||
- netdev_uses_dsa(dev))
+ !is_valid_ether_addr(dev->dev_addr))
return -EINVAL;
+ /* Also don't allow bridging of net devices that are DSA masters, since
+ * the bridge layer rx_handler prevents the DSA fake ethertype handler
+ * to be invoked, so we don't get the chance to strip off and parse the
+ * DSA switch tag protocol header (the bridge layer just returns
+ * RX_HANDLER_CONSUMED, stopping RX processing for these frames).
+ * The only case where that would not be an issue is when bridging can
+ * already be offloaded, such as when the DSA master is itself a DSA
+ * or plain switchdev port, and is bridged only with other ports from
+ * the same hardware device.
+ */
+ if (netdev_uses_dsa(dev)) {
+ list_for_each_entry(p, &br->port_list, list) {
+ if (!netdev_port_same_parent_id(dev, p->dev)) {
+ NL_SET_ERR_MSG(extack,
+ "Cannot do software bridging with a DSA master");
+ return -EINVAL;
+ }
+ }
+ }
+
/* No bridging of bridges */
if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) {
NL_SET_ERR_MSG(extack,
@@ -618,7 +632,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
if (err)
goto err3;
- err = netdev_rx_handler_register(dev, br_handle_frame, p);
+ err = netdev_rx_handler_register(dev, br_get_rx_handler(dev), p);
if (err)
goto err4;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index d5c34f36f0f4..59a318b9f646 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -17,6 +17,7 @@
#endif
#include <linux/neighbour.h>
#include <net/arp.h>
+#include <net/dsa.h>
#include <linux/export.h>
#include <linux/rculist.h>
#include "br_private.h"
@@ -257,7 +258,7 @@ frame_finish:
* Return NULL if skb is handled
* note: already called with rcu_read_lock
*/
-rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
+static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
{
struct net_bridge_port *p;
struct sk_buff *skb = *pskb;
@@ -359,3 +360,23 @@ drop:
}
return RX_HANDLER_CONSUMED;
}
+
+/* This function has no purpose other than to appease the br_port_get_rcu/rtnl
+ * helpers which identify bridged ports according to the rx_handler installed
+ * on them (so there _needs_ to be a bridge rx_handler even if we don't need it
+ * to do anything useful). This bridge won't support traffic to/from the stack,
+ * but only hardware bridging. So return RX_HANDLER_PASS so we don't steal
+ * frames from the ETH_P_XDSA packet_type handler.
+ */
+static rx_handler_result_t br_handle_frame_dummy(struct sk_buff **pskb)
+{
+ return RX_HANDLER_PASS;
+}
+
+rx_handler_func_t *br_get_rx_handler(const struct net_device *dev)
+{
+ if (netdev_uses_dsa(dev))
+ return br_handle_frame_dummy;
+
+ return br_handle_frame;
+}
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
index 503896638be0..397e7f710772 100644
--- a/net/bridge/br_mrp_netlink.c
+++ b/net/bridge/br_mrp_netlink.c
@@ -28,7 +28,7 @@ int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
int err;
if (br->stp_enabled != BR_NO_STP) {
- NL_SET_ERR_MSG_MOD(extack, "MRP can't be enabled if STP is already enabled\n");
+ NL_SET_ERR_MSG_MOD(extack, "MRP can't be enabled if STP is already enabled");
return -EINVAL;
}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index a774e19c41bb..240e260e3461 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -615,6 +615,7 @@ int br_process_vlan_info(struct net_bridge *br,
v - 1, rtm_cmd);
v_change_start = 0;
}
+ cond_resched();
}
/* v_change_start is set only if the last/whole range changed */
if (v_change_start)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index c35647cb138a..7501be4eeba0 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -598,10 +598,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
struct sk_buff *skb)
{
- struct netpoll *np = p->np;
-
- if (np)
- netpoll_send_skb(np, skb);
+ netpoll_send_skb(p->np, skb);
}
int br_netpoll_enable(struct net_bridge_port *p);
@@ -705,16 +702,16 @@ int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev);
/* br_input.c */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
-rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
+rx_handler_func_t *br_get_rx_handler(const struct net_device *dev);
static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
{
- return rcu_dereference(dev->rx_handler) == br_handle_frame;
+ return rcu_dereference(dev->rx_handler) == br_get_rx_handler(dev);
}
static inline bool br_rx_handler_check_rtnl(const struct net_device *dev)
{
- return rcu_dereference_rtnl(dev->rx_handler) == br_handle_frame;
+ return rcu_dereference_rtnl(dev->rx_handler) == br_get_rx_handler(dev);
}
static inline struct net_bridge_port *br_port_get_check_rcu(const struct net_device *dev)
@@ -1331,7 +1328,7 @@ static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
static inline bool br_mrp_enabled(struct net_bridge *br)
{
- return 0;
+ return false;
}
static inline void br_mrp_port_del(struct net_bridge *br,
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index a42850b7eb9a..ba55851fe132 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -203,7 +203,7 @@ int br_stp_set_enabled(struct net_bridge *br, unsigned long val,
if (br_mrp_enabled(br)) {
NL_SET_ERR_MSG_MOD(extack,
- "STP can't be enabled if MRP is already enabled\n");
+ "STP can't be enabled if MRP is already enabled");
return -EINVAL;
}