mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-04-16 04:13:31 +00:00
696 lines
18 KiB
Diff
696 lines
18 KiB
Diff
--- a/include/linux/if_bridge.h
|
|
+++ b/include/linux/if_bridge.h
|
|
@@ -149,4 +149,37 @@ br_port_flag_is_set(const struct net_dev
|
|
}
|
|
#endif
|
|
|
|
+extern struct net_device *br_port_dev_get(struct net_device *dev,
|
|
+ unsigned char *addr,
|
|
+ struct sk_buff *skb,
|
|
+ unsigned int cookie);
|
|
+extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr);
|
|
+extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
|
|
+ const char *addr,
|
|
+ __u16 vid);
|
|
+extern void br_fdb_update_register_notify(struct notifier_block *nb);
|
|
+extern void br_fdb_update_unregister_notify(struct notifier_block *nb);
|
|
+
|
|
+typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev,
|
|
+ struct sk_buff *skb,
|
|
+ unsigned char *addr,
|
|
+ unsigned int cookie);
|
|
+extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook;
|
|
+
|
|
+#define BR_FDB_EVENT_ADD 0x01
|
|
+#define BR_FDB_EVENT_DEL 0x02
|
|
+
|
|
+struct br_fdb_event {
|
|
+ struct net_device *dev;
|
|
+ unsigned char addr[6];
|
|
+ unsigned char is_local;
|
|
+};
|
|
+extern void br_fdb_register_notify(struct notifier_block *nb);
|
|
+extern void br_fdb_unregister_notify(struct notifier_block *nb);
|
|
+
|
|
+typedef struct net_bridge_port *br_get_dst_hook_t(
|
|
+ const struct net_bridge_port *src,
|
|
+ struct sk_buff **skb);
|
|
+extern br_get_dst_hook_t __rcu *br_get_dst_hook;
|
|
+
|
|
#endif
|
|
--- a/include/linux/netdevice.h
|
|
+++ b/include/linux/netdevice.h
|
|
@@ -2661,6 +2661,8 @@ enum netdev_cmd {
|
|
NETDEV_CVLAN_FILTER_DROP_INFO,
|
|
NETDEV_SVLAN_FILTER_PUSH_INFO,
|
|
NETDEV_SVLAN_FILTER_DROP_INFO,
|
|
+ NETDEV_BR_JOIN,
|
|
+ NETDEV_BR_LEAVE,
|
|
};
|
|
const char *netdev_cmd_to_name(enum netdev_cmd cmd);
|
|
|
|
--- a/include/linux/ppp_channel.h
|
|
+++ b/include/linux/ppp_channel.h
|
|
@@ -32,6 +32,17 @@ struct ppp_channel_ops {
|
|
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
|
|
int (*flow_offload_check)(struct ppp_channel *, struct flow_offload_hw_path *);
|
|
#endif
|
|
+
|
|
+ /* Get channel protocol type, one of PX_PROTO_XYZ or specific to
|
|
+ * the channel subtype
|
|
+ */
|
|
+ int (*get_channel_protocol)(struct ppp_channel *);
|
|
+ /* Get channel protocol version */
|
|
+ int (*get_channel_protocol_ver)(struct ppp_channel *);
|
|
+ /* Hold the channel from being destroyed */
|
|
+ void (*hold)(struct ppp_channel *);
|
|
+ /* Release hold on the channel */
|
|
+ void (*release)(struct ppp_channel *);
|
|
};
|
|
|
|
struct ppp_channel {
|
|
@@ -84,5 +95,53 @@ extern char *ppp_dev_name(struct ppp_cha
|
|
* that ppp_unregister_channel returns.
|
|
*/
|
|
|
|
+/* Call this to obtain the underlying protocol of the PPP channel,
|
|
+ * e.g. PX_PROTO_OE
|
|
+ */
|
|
+extern int ppp_channel_get_protocol(struct ppp_channel *);
|
|
+
|
|
+/* Call this get protocol version */
|
|
+extern int ppp_channel_get_proto_version(struct ppp_channel *);
|
|
+
|
|
+/* Call this to hold a channel */
|
|
+extern bool ppp_channel_hold(struct ppp_channel *);
|
|
+
|
|
+/* Call this to release a hold you have upon a channel */
|
|
+extern void ppp_channel_release(struct ppp_channel *);
|
|
+
|
|
+/* Release hold on PPP channels */
|
|
+extern void ppp_release_channels(struct ppp_channel *channels[],
|
|
+ unsigned int chan_sz);
|
|
+
|
|
+/* Hold PPP channels for the PPP device */
|
|
+extern int ppp_hold_channels(struct net_device *dev,
|
|
+ struct ppp_channel *channels[],
|
|
+ unsigned int chan_sz);
|
|
+/* Test if ppp xmit lock is locked */
|
|
+extern bool ppp_is_xmit_locked(struct net_device *dev);
|
|
+
|
|
+/* Hold PPP channels for the PPP device */
|
|
+extern int __ppp_hold_channels(struct net_device *dev,
|
|
+ struct ppp_channel *channels[],
|
|
+ unsigned int chan_sz);
|
|
+
|
|
+/* Test if the ppp device is a multi-link ppp device */
|
|
+extern int ppp_is_multilink(struct net_device *dev);
|
|
+
|
|
+/* Test if the ppp device is a multi-link ppp device */
|
|
+extern int __ppp_is_multilink(struct net_device *dev);
|
|
+
|
|
+/* Update statistics of the PPP net_device by incrementing related
|
|
+ * statistics field value with corresponding parameter
|
|
+ */
|
|
+extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets,
|
|
+ unsigned long rx_bytes, unsigned long tx_packets,
|
|
+ unsigned long tx_bytes, unsigned long rx_errors,
|
|
+ unsigned long tx_errors, unsigned long rx_dropped,
|
|
+ unsigned long tx_dropped);
|
|
+
|
|
+/* Get the device index associated with a channel, or 0, if none */
|
|
+extern int ppp_dev_index(struct ppp_channel *);
|
|
+
|
|
#endif /* __KERNEL__ */
|
|
#endif
|
|
--- a/drivers/net/phy/phy.c
|
|
+++ b/drivers/net/phy/phy.c
|
|
@@ -728,6 +728,7 @@ void phy_stop_machine(struct phy_device
|
|
phydev->state = PHY_UP;
|
|
mutex_unlock(&phydev->lock);
|
|
}
|
|
+EXPORT_SYMBOL_GPL(phy_stop_machine);
|
|
|
|
/**
|
|
* phy_error - enter HALTED state for this PHY device
|
|
--- a/drivers/net/ppp/ppp_generic.c
|
|
+++ b/drivers/net/ppp/ppp_generic.c
|
|
@@ -3359,6 +3359,318 @@ static void *unit_find(struct idr *p, in
|
|
return idr_find(p, n);
|
|
}
|
|
|
|
+/* Return the PPP net device index */
|
|
+int ppp_dev_index(struct ppp_channel *chan)
|
|
+{
|
|
+ struct channel *pch = chan->ppp;
|
|
+ int ifindex = 0;
|
|
+
|
|
+ if (pch) {
|
|
+ read_lock_bh(&pch->upl);
|
|
+ if (pch->ppp && pch->ppp->dev)
|
|
+ ifindex = pch->ppp->dev->ifindex;
|
|
+ read_unlock_bh(&pch->upl);
|
|
+ }
|
|
+ return ifindex;
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_dev_index);
|
|
+
|
|
+/* Updates the PPP interface statistics. */
|
|
+void ppp_update_stats(struct net_device *dev, unsigned long rx_packets,
|
|
+ unsigned long rx_bytes, unsigned long tx_packets,
|
|
+ unsigned long tx_bytes, unsigned long rx_errors,
|
|
+ unsigned long tx_errors, unsigned long rx_dropped,
|
|
+ unsigned long tx_dropped)
|
|
+{
|
|
+ struct ppp *ppp;
|
|
+
|
|
+ if (!dev)
|
|
+ return;
|
|
+
|
|
+ if (dev->type != ARPHRD_PPP)
|
|
+ return;
|
|
+
|
|
+ ppp = netdev_priv(dev);
|
|
+
|
|
+ ppp_xmit_lock(ppp);
|
|
+ ppp->stats64.tx_packets += tx_packets;
|
|
+ ppp->stats64.tx_bytes += tx_bytes;
|
|
+ ppp->dev->stats.tx_errors += tx_errors;
|
|
+ ppp->dev->stats.tx_dropped += tx_dropped;
|
|
+ if (tx_packets)
|
|
+ ppp->last_xmit = jiffies;
|
|
+ ppp_xmit_unlock(ppp);
|
|
+
|
|
+ ppp_recv_lock(ppp);
|
|
+ ppp->stats64.rx_packets += rx_packets;
|
|
+ ppp->stats64.rx_bytes += rx_bytes;
|
|
+ ppp->dev->stats.rx_errors += rx_errors;
|
|
+ ppp->dev->stats.rx_dropped += rx_dropped;
|
|
+ if (rx_packets)
|
|
+ ppp->last_recv = jiffies;
|
|
+ ppp_recv_unlock(ppp);
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_update_stats);
|
|
+
|
|
+/* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if
|
|
+ * the device is not PPP.
|
|
+ */
|
|
+int ppp_is_multilink(struct net_device *dev)
|
|
+{
|
|
+ struct ppp *ppp;
|
|
+ unsigned int flags;
|
|
+
|
|
+ if (!dev)
|
|
+ return -1;
|
|
+
|
|
+ if (dev->type != ARPHRD_PPP)
|
|
+ return -1;
|
|
+
|
|
+ ppp = netdev_priv(dev);
|
|
+ ppp_lock(ppp);
|
|
+ flags = ppp->flags;
|
|
+ ppp_unlock(ppp);
|
|
+
|
|
+ if (flags & SC_MULTILINK)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_is_multilink);
|
|
+
|
|
+/* __ppp_is_multilink()
|
|
+ * Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0
|
|
+ * if the device is not PPP. Caller should acquire ppp_lock before calling
|
|
+ * this function
|
|
+ */
|
|
+int __ppp_is_multilink(struct net_device *dev)
|
|
+{
|
|
+ struct ppp *ppp;
|
|
+ unsigned int flags;
|
|
+
|
|
+ if (!dev)
|
|
+ return -1;
|
|
+
|
|
+ if (dev->type != ARPHRD_PPP)
|
|
+ return -1;
|
|
+
|
|
+ ppp = netdev_priv(dev);
|
|
+ flags = ppp->flags;
|
|
+
|
|
+ if (flags & SC_MULTILINK)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(__ppp_is_multilink);
|
|
+
|
|
+/* ppp_channel_get_protocol()
|
|
+ * Call this to obtain the underlying protocol of the PPP channel,
|
|
+ * e.g. PX_PROTO_OE
|
|
+ *
|
|
+ * NOTE: Some channels do not use PX sockets so the protocol value may be very
|
|
+ * different for them.
|
|
+ * NOTE: -1 indicates failure.
|
|
+ * NOTE: Once you know the channel protocol you may then either cast 'chan' to
|
|
+ * its sub-class or use the channel protocol specific API's as provided by that
|
|
+ * channel sub type.
|
|
+ */
|
|
+int ppp_channel_get_protocol(struct ppp_channel *chan)
|
|
+{
|
|
+ if (!chan->ops->get_channel_protocol)
|
|
+ return -1;
|
|
+
|
|
+ return chan->ops->get_channel_protocol(chan);
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_channel_get_protocol);
|
|
+
|
|
+/* ppp_channel_get_proto_version()
|
|
+ * Call this to get channel protocol version
|
|
+ */
|
|
+int ppp_channel_get_proto_version(struct ppp_channel *chan)
|
|
+{
|
|
+ if (!chan->ops->get_channel_protocol_ver)
|
|
+ return -1;
|
|
+
|
|
+ return chan->ops->get_channel_protocol_ver(chan);
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_channel_get_proto_version);
|
|
+
|
|
+/* ppp_channel_hold()
|
|
+ * Call this to hold a channel.
|
|
+ *
|
|
+ * Returns true on success or false if the hold could not happen.
|
|
+ *
|
|
+ * NOTE: chan must be protected against destruction during this call -
|
|
+ * either by correct locking etc. or because you already have an implicit
|
|
+ * or explicit hold to the channel already and this is an additional hold.
|
|
+ */
|
|
+bool ppp_channel_hold(struct ppp_channel *chan)
|
|
+{
|
|
+ if (!chan->ops->hold)
|
|
+ return false;
|
|
+
|
|
+ chan->ops->hold(chan);
|
|
+ return true;
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_channel_hold);
|
|
+
|
|
+/* ppp_channel_release()
|
|
+ * Call this to release a hold you have upon a channel
|
|
+ */
|
|
+void ppp_channel_release(struct ppp_channel *chan)
|
|
+{
|
|
+ chan->ops->release(chan);
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_channel_release);
|
|
+
|
|
+/* ppp_hold_channels()
|
|
+ * Returns the PPP channels of the PPP device, storing each one into
|
|
+ * channels[].
|
|
+ *
|
|
+ * channels[] has chan_sz elements.
|
|
+ * This function returns the number of channels stored, up to chan_sz.
|
|
+ * It will return < 0 if the device is not PPP.
|
|
+ *
|
|
+ * You MUST release the channels using ppp_release_channels().
|
|
+ */
|
|
+int ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[],
|
|
+ unsigned int chan_sz)
|
|
+{
|
|
+ struct ppp *ppp;
|
|
+ int c;
|
|
+ struct channel *pch;
|
|
+
|
|
+ if (!dev)
|
|
+ return -1;
|
|
+
|
|
+ if (dev->type != ARPHRD_PPP)
|
|
+ return -1;
|
|
+
|
|
+ ppp = netdev_priv(dev);
|
|
+
|
|
+ c = 0;
|
|
+ ppp_lock(ppp);
|
|
+ list_for_each_entry(pch, &ppp->channels, clist) {
|
|
+ struct ppp_channel *chan;
|
|
+
|
|
+ if (!pch->chan) {
|
|
+ /* Channel is going / gone away */
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (c == chan_sz) {
|
|
+ /* No space to record channel */
|
|
+ ppp_unlock(ppp);
|
|
+ return c;
|
|
+ }
|
|
+
|
|
+ /* Hold the channel, if supported */
|
|
+ chan = pch->chan;
|
|
+ if (!chan->ops->hold)
|
|
+ continue;
|
|
+
|
|
+ chan->ops->hold(chan);
|
|
+
|
|
+ /* Record the channel */
|
|
+ channels[c++] = chan;
|
|
+ }
|
|
+ ppp_unlock(ppp);
|
|
+ return c;
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_hold_channels);
|
|
+
|
|
+/* __ppp_hold_channels()
|
|
+ * Returns the PPP channels of the PPP device, storing each one
|
|
+ * into channels[].
|
|
+ *
|
|
+ * channels[] has chan_sz elements.
|
|
+ * This function returns the number of channels stored, up to chan_sz.
|
|
+ * It will return < 0 if the device is not PPP.
|
|
+ *
|
|
+ * You MUST acquire ppp_lock and release the channels using
|
|
+ * ppp_release_channels().
|
|
+ */
|
|
+int __ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[],
|
|
+ unsigned int chan_sz)
|
|
+{
|
|
+ struct ppp *ppp;
|
|
+ int c;
|
|
+ struct channel *pch;
|
|
+
|
|
+ if (!dev)
|
|
+ return -1;
|
|
+
|
|
+ if (dev->type != ARPHRD_PPP)
|
|
+ return -1;
|
|
+
|
|
+ ppp = netdev_priv(dev);
|
|
+
|
|
+ c = 0;
|
|
+ list_for_each_entry(pch, &ppp->channels, clist) {
|
|
+ struct ppp_channel *chan;
|
|
+
|
|
+ if (!pch->chan) {
|
|
+ /* Channel is going / gone away*/
|
|
+ continue;
|
|
+ }
|
|
+ if (c == chan_sz) {
|
|
+ /* No space to record channel */
|
|
+ return c;
|
|
+ }
|
|
+
|
|
+ /* Hold the channel, if supported */
|
|
+ chan = pch->chan;
|
|
+ if (!chan->ops->hold)
|
|
+ continue;
|
|
+
|
|
+ chan->ops->hold(chan);
|
|
+
|
|
+ /* Record the channel */
|
|
+ channels[c++] = chan;
|
|
+ }
|
|
+ return c;
|
|
+}
|
|
+EXPORT_SYMBOL(__ppp_hold_channels);
|
|
+
|
|
+/* ppp_release_channels()
|
|
+ * Releases channels
|
|
+ */
|
|
+void ppp_release_channels(struct ppp_channel *channels[], unsigned int chan_sz)
|
|
+{
|
|
+ unsigned int c;
|
|
+
|
|
+ for (c = 0; c < chan_sz; ++c) {
|
|
+ struct ppp_channel *chan;
|
|
+
|
|
+ chan = channels[c];
|
|
+ chan->ops->release(chan);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_release_channels);
|
|
+
|
|
+/* Check if ppp xmit lock is on hold */
|
|
+bool ppp_is_xmit_locked(struct net_device *dev)
|
|
+{
|
|
+ struct ppp *ppp;
|
|
+
|
|
+ if (!dev)
|
|
+ return false;
|
|
+
|
|
+ if (dev->type != ARPHRD_PPP)
|
|
+ return false;
|
|
+
|
|
+ ppp = netdev_priv(dev);
|
|
+ if (!ppp)
|
|
+ return false;
|
|
+
|
|
+ if (spin_is_locked(&(ppp)->wlock))
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+EXPORT_SYMBOL(ppp_is_xmit_locked);
|
|
+
|
|
/* Module/initialization stuff */
|
|
|
|
module_init(ppp_init);
|
|
--- a/net/bridge/br_if.c
|
|
+++ b/net/bridge/br_if.c
|
|
@@ -26,6 +26,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
|
|
@@ -695,6 +699,8 @@ int br_add_if(struct net_bridge *br, str
|
|
|
|
kobject_uevent(&p->kobj, KOBJ_ADD);
|
|
|
|
+ call_netdevice_notifiers(NETDEV_BR_JOIN, dev);
|
|
+
|
|
return 0;
|
|
|
|
err7:
|
|
@@ -731,6 +737,8 @@ int br_del_if(struct net_bridge *br, str
|
|
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.
|
|
@@ -785,6 +793,65 @@ void br_dev_update_stats(struct net_devi
|
|
}
|
|
EXPORT_SYMBOL_GPL(br_dev_update_stats);
|
|
|
|
+/* 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_find_rcu(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);
|
|
+
|
|
bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
|
|
{
|
|
struct net_bridge_port *p;
|
|
--- a/net/bridge/br_fdb.c
|
|
+++ b/net/bridge/br_fdb.c
|
|
@@ -37,6 +37,33 @@ static int fdb_insert(struct net_bridge
|
|
static void fdb_notify(struct net_bridge *br,
|
|
const struct net_bridge_fdb_entry *, int, bool);
|
|
|
|
+ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list);
|
|
+ATOMIC_NOTIFIER_HEAD(br_fdb_update_notifier_list);
|
|
+
|
|
+void br_fdb_register_notify(struct notifier_block *nb)
|
|
+{
|
|
+ atomic_notifier_chain_register(&br_fdb_notifier_list, nb);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(br_fdb_register_notify);
|
|
+
|
|
+void br_fdb_unregister_notify(struct notifier_block *nb)
|
|
+{
|
|
+ atomic_notifier_chain_unregister(&br_fdb_notifier_list, nb);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(br_fdb_unregister_notify);
|
|
+
|
|
+void br_fdb_update_register_notify(struct notifier_block *nb)
|
|
+{
|
|
+ atomic_notifier_chain_register(&br_fdb_update_notifier_list, nb);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(br_fdb_update_register_notify);
|
|
+
|
|
+void br_fdb_update_unregister_notify(struct notifier_block *nb)
|
|
+{
|
|
+ atomic_notifier_chain_unregister(&br_fdb_update_notifier_list, nb);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify);
|
|
+
|
|
int __init br_fdb_init(void)
|
|
{
|
|
br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
|
|
@@ -337,6 +364,7 @@ void br_fdb_cleanup(struct work_struct *
|
|
unsigned long delay = hold_time(br);
|
|
unsigned long work_delay = delay;
|
|
unsigned long now = jiffies;
|
|
+ u8 mac_addr[6];
|
|
|
|
/* this part is tricky, in order to avoid blocking learning and
|
|
* consequently forwarding, we rely on rcu to delete objects with
|
|
@@ -353,8 +381,11 @@ void br_fdb_cleanup(struct work_struct *
|
|
work_delay = min(work_delay, this_timer - now);
|
|
} else {
|
|
spin_lock_bh(&br->hash_lock);
|
|
- if (!hlist_unhashed(&f->fdb_node))
|
|
+ if (!hlist_unhashed(&f->fdb_node)) {
|
|
fdb_delete(br, f, true);
|
|
+ atomic_notifier_call_chain(
|
|
+ &br_fdb_update_notifier_list, 0, (void *)mac_addr);
|
|
+ }
|
|
spin_unlock_bh(&br->hash_lock);
|
|
}
|
|
}
|
|
@@ -587,6 +618,8 @@ void br_fdb_update(struct net_bridge *br
|
|
/* Take over HW learned entry */
|
|
if (unlikely(fdb->added_by_external_learn))
|
|
fdb->added_by_external_learn = 0;
|
|
+ atomic_notifier_call_chain(
|
|
+ &br_fdb_update_notifier_list, 0, (void *)addr);
|
|
}
|
|
if (now != fdb->updated)
|
|
fdb->updated = now;
|
|
@@ -696,6 +729,23 @@ static void fdb_notify(struct net_bridge
|
|
struct sk_buff *skb;
|
|
int err = -ENOBUFS;
|
|
|
|
+ if (fdb->dst) {
|
|
+ int event;
|
|
+ struct br_fdb_event fdb_event;
|
|
+
|
|
+ if (type == RTM_NEWNEIGH)
|
|
+ event = BR_FDB_EVENT_ADD;
|
|
+ else
|
|
+ event = BR_FDB_EVENT_DEL;
|
|
+
|
|
+ fdb_event.dev = fdb->dst->dev;
|
|
+ ether_addr_copy(fdb_event.addr, fdb->key.addr.addr);
|
|
+ fdb_event.is_local = fdb->is_local;
|
|
+ atomic_notifier_call_chain(&br_fdb_notifier_list,
|
|
+ event,
|
|
+ (void *)&fdb_event);
|
|
+ }
|
|
+
|
|
if (swdev_notify)
|
|
br_switchdev_fdb_notify(br, fdb, type);
|
|
|
|
@@ -1212,3 +1262,41 @@ void br_fdb_clear_offload(const struct n
|
|
spin_unlock_bh(&p->br->hash_lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(br_fdb_clear_offload);
|
|
+
|
|
+/* Refresh FDB entries for bridge packets being forwarded by offload engines */
|
|
+void br_refresh_fdb_entry(struct net_device *dev, const char *addr)
|
|
+{
|
|
+ struct net_bridge_port *p = br_port_get_rcu(dev);
|
|
+
|
|
+ if (!p || p->state == BR_STATE_DISABLED)
|
|
+ return;
|
|
+
|
|
+ if (!is_valid_ether_addr(addr)) {
|
|
+ pr_info("bridge: Attempt to refresh with invalid ether address %pM\n",
|
|
+ addr);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rcu_read_lock();
|
|
+ br_fdb_update(p->br, p, addr, 0, true);
|
|
+ rcu_read_unlock();
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(br_refresh_fdb_entry);
|
|
+
|
|
+/* Look up the MAC address in the device's bridge fdb table */
|
|
+struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
|
|
+ const char *addr, __u16 vid)
|
|
+{
|
|
+ struct net_bridge_port *p = br_port_get_rcu(dev);
|
|
+ struct net_bridge_fdb_entry *fdb;
|
|
+
|
|
+ if (!p || p->state == BR_STATE_DISABLED)
|
|
+ return NULL;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid);
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return fdb;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(br_fdb_has_entry);
|
|
--- a/net/bridge/br_private.h
|
|
+++ b/net/bridge/br_private.h
|
|
@@ -1269,4 +1269,7 @@ void br_do_proxy_suppress_arp(struct sk_
|
|
void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
|
|
u16 vid, struct net_bridge_port *p, struct nd_msg *msg);
|
|
struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m);
|
|
+
|
|
+#define __br_get(__hook, __default, __args ...) \
|
|
+ (__hook ? (__hook(__args)) : (__default))
|
|
#endif
|