--- zzzz-none-000/linux-4.4.60/net/bridge/br_if.c 2017-04-08 07:53:53.000000000 +0000 +++ scorpion-1750e-727/linux-4.4.60/net/bridge/br_if.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,4 +1,10 @@ /* + ************************************************************************** + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + ************************************************************************** + */ + +/* * Userspace interface * Linux ethernet bridge * @@ -28,6 +34,10 @@ #include "br_private.h" +/* Hook for external forwarding logic */ +br_port_dev_get_hook_t __rcu *br_port_dev_get_hook __read_mostly; +EXPORT_SYMBOL_GPL(br_port_dev_get_hook); + /* * Determine initial path cost based on speed. * using recommendations from 802.1d standard @@ -242,6 +252,7 @@ nbp_delete_promisc(p); spin_lock_bh(&br->lock); + br_isol_remove_port(p); br_stp_disable_port(p); spin_unlock_bh(&br->lock); @@ -530,6 +541,7 @@ dev_set_mtu(br->dev, br_min_mtu(br)); kobject_uevent(&p->kobj, KOBJ_ADD); + call_netdevice_notifiers(NETDEV_BR_JOIN, dev); return 0; @@ -561,6 +573,8 @@ if (!p || p->br != br) return -EINVAL; + call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); + /* Since more than one interface can be attached to a bridge, * there still maybe an alternate path for netconsole to use; * therefore there is no reason for a NETDEV_RELEASE event. @@ -588,3 +602,86 @@ if (mask & BR_AUTO_MASK) nbp_update_port_count(br); } + +/* br_port_dev_get() + * If a skb is provided, and the br_port_dev_get_hook_t hook exists, + * use that to try and determine the egress port for that skb. + * If not, or no egress port could be determined, use the given addr + * to identify the port to which it is reachable, + * returing a reference to the net device associated with that port. + * + * NOTE: Return NULL if given dev is not a bridge or the mac has no + * associated port. + */ +struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr, + struct sk_buff *skb, + unsigned int cookie) +{ + struct net_bridge_fdb_entry *fdbe; + struct net_bridge *br; + struct net_device *netdev = NULL; + + /* Is this a bridge? */ + if (!(dev->priv_flags & IFF_EBRIDGE)) + return NULL; + + rcu_read_lock(); + + /* If the hook exists and the skb isn't NULL, try and get the port */ + if (skb) { + br_port_dev_get_hook_t *port_dev_get_hook; + + port_dev_get_hook = rcu_dereference(br_port_dev_get_hook); + if (port_dev_get_hook) { + struct net_bridge_port *pdst = + __br_get(port_dev_get_hook, NULL, dev, skb, + addr, cookie); + if (pdst) { + dev_hold(pdst->dev); + netdev = pdst->dev; + goto out; + } + } + } + + /* Either there is no hook, or can't + * determine the port to use - fall back to using FDB + */ + + br = netdev_priv(dev); + + /* Lookup the fdb entry and get reference to the port dev */ + fdbe = __br_fdb_get(br, addr, 0); + if (fdbe && fdbe->dst) { + netdev = fdbe->dst->dev; /* port device */ + dev_hold(netdev); + } +out: + rcu_read_unlock(); + return netdev; +} +EXPORT_SYMBOL_GPL(br_port_dev_get); + +/* Update bridge statistics for bridge packets processed by offload engines */ +void br_dev_update_stats(struct net_device *dev, + struct rtnl_link_stats64 *nlstats) +{ + struct net_bridge *br; + struct pcpu_sw_netstats *stats; + + /* Is this a bridge? */ + if (!(dev->priv_flags & IFF_EBRIDGE)) + return; + + br = netdev_priv(dev); + stats = per_cpu_ptr(br->stats, 0); + + u64_stats_update_begin(&stats->syncp); + stats->rx_packets += nlstats->rx_packets; + stats->rx_bytes += nlstats->rx_bytes; + stats->tx_packets += nlstats->tx_packets; + stats->tx_bytes += nlstats->tx_bytes; + u64_stats_update_end(&stats->syncp); +} +EXPORT_SYMBOL_GPL(br_dev_update_stats); +