net: bridge: allow enslaving some DSA master network devices
Commit 8db0a2ee2c
("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>
This commit is contained in:
parent
90d9834ecd
commit
9eb8eff0cf
|
@ -651,7 +651,7 @@ struct dsa_switch_driver {
|
|||
struct net_device *dsa_dev_to_net_device(struct device *dev);
|
||||
|
||||
/* Keep inline for faster access in hot path */
|
||||
static inline bool netdev_uses_dsa(struct net_device *dev)
|
||||
static inline bool netdev_uses_dsa(const struct net_device *dev)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_NET_DSA)
|
||||
return dev->dsa_ptr && dev->dsa_ptr->rcv;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -702,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)
|
||||
|
|
Loading…
Reference in a new issue