diff options
Diffstat (limited to 'net/bridge')
-rw-r--r-- | net/bridge/br_if.c | 32 | ||||
-rw-r--r-- | net/bridge/br_input.c | 23 | ||||
-rw-r--r-- | net/bridge/br_mrp_netlink.c | 2 | ||||
-rw-r--r-- | net/bridge/br_netlink.c | 1 | ||||
-rw-r--r-- | net/bridge/br_private.h | 13 | ||||
-rw-r--r-- | net/bridge/br_stp_if.c | 2 |
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; } |