aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_if.c
diff options
context:
space:
mode:
authorVladimir Oltean <vladimir.oltean@nxp.com>2020-05-10 19:37:40 +0300
committerJakub Kicinski <kuba@kernel.org>2020-05-10 19:52:33 -0700
commit9eb8eff0cf2f1e1afc0756bb30cb9746ba90dd07 (patch)
tree6b41633b856834a8cc2a05bdfde065475b19f702 /net/bridge/br_if.c
parentMerge branch 'net-hns3-misc-updates-for-next' (diff)
downloadlinux-dev-9eb8eff0cf2f1e1afc0756bb30cb9746ba90dd07.tar.xz
linux-dev-9eb8eff0cf2f1e1afc0756bb30cb9746ba90dd07.zip
net: bridge: allow enslaving some DSA master network devices
Commit 8db0a2ee2c63 ("net: bridge: reject DSA-enabled master netdevices as bridge members") added a special check in br_if.c in order to check for a DSA master network device with a tagging protocol configured. This was done because back then, such devices, once enslaved in a bridge would become inoperative and would not pass DSA tagged traffic anymore due to br_handle_frame returning RX_HANDLER_CONSUMED. But right now we have valid use cases which do require bridging of DSA masters. One such example is when the DSA master ports are DSA switch ports themselves (in a disjoint tree setup). This should be completely equivalent, functionally speaking, from having multiple DSA switches hanging off of the ports of a switchdev driver. So we should allow the enslaving of DSA tagged master network devices. Instead of the regular br_handle_frame(), install a new function br_handle_frame_dummy() on these DSA masters, which returns RX_HANDLER_PASS in order to call into the DSA specific tagging protocol handlers, and lift the restriction from br_add_if. Suggested-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Suggested-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Tested-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/bridge/br_if.c')
-rw-r--r--net/bridge/br_if.c32
1 files changed, 23 insertions, 9 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;