2019-05-27 14:55:01 +08:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Linux ethernet bridge
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Lennert Buytenhek <buytenh@gnu.org>
|
|
|
|
*/
|
|
|
|
#ifndef _LINUX_IF_BRIDGE_H
|
|
|
|
#define _LINUX_IF_BRIDGE_H
|
|
|
|
|
|
|
|
|
|
|
|
#include <linux/netdevice.h>
|
2012-10-13 17:46:48 +08:00
|
|
|
#include <uapi/linux/if_bridge.h>
|
2014-11-28 21:34:22 +08:00
|
|
|
#include <linux/bitops.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-06-08 00:26:28 +08:00
|
|
|
struct br_ip {
|
|
|
|
union {
|
|
|
|
__be32 ip4;
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
struct in6_addr ip6;
|
2020-09-22 15:30:15 +08:00
|
|
|
#endif
|
|
|
|
} src;
|
|
|
|
union {
|
|
|
|
__be32 ip4;
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
struct in6_addr ip6;
|
2014-06-08 00:26:28 +08:00
|
|
|
#endif
|
2020-10-29 07:38:31 +08:00
|
|
|
unsigned char mac_addr[ETH_ALEN];
|
2020-09-22 15:30:17 +08:00
|
|
|
} dst;
|
2014-06-08 00:26:28 +08:00
|
|
|
__be16 proto;
|
|
|
|
__u16 vid;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct br_ip_list {
|
|
|
|
struct list_head list;
|
|
|
|
struct br_ip addr;
|
|
|
|
};
|
|
|
|
|
2014-11-28 21:34:22 +08:00
|
|
|
#define BR_HAIRPIN_MODE BIT(0)
|
|
|
|
#define BR_BPDU_GUARD BIT(1)
|
|
|
|
#define BR_ROOT_BLOCK BIT(2)
|
|
|
|
#define BR_MULTICAST_FAST_LEAVE BIT(3)
|
|
|
|
#define BR_ADMIN_COST BIT(4)
|
|
|
|
#define BR_LEARNING BIT(5)
|
|
|
|
#define BR_FLOOD BIT(6)
|
|
|
|
#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING)
|
|
|
|
#define BR_PROMISC BIT(7)
|
|
|
|
#define BR_PROXYARP BIT(8)
|
2014-11-28 21:34:23 +08:00
|
|
|
#define BR_LEARNING_SYNC BIT(9)
|
2015-03-04 18:54:21 +08:00
|
|
|
#define BR_PROXYARP_WIFI BIT(10)
|
2016-08-31 21:36:52 +08:00
|
|
|
#define BR_MCAST_FLOOD BIT(11)
|
2017-01-22 04:01:32 +08:00
|
|
|
#define BR_MULTICAST_TO_UNICAST BIT(12)
|
2017-02-01 14:59:53 +08:00
|
|
|
#define BR_VLAN_TUNNEL BIT(13)
|
2017-04-26 21:48:09 +08:00
|
|
|
#define BR_BCAST_FLOOD BIT(14)
|
2017-10-07 13:12:37 +08:00
|
|
|
#define BR_NEIGH_SUPPRESS BIT(15)
|
2018-05-24 16:56:48 +08:00
|
|
|
#define BR_ISOLATED BIT(16)
|
2020-04-26 21:22:00 +08:00
|
|
|
#define BR_MRP_AWARE BIT(17)
|
|
|
|
#define BR_MRP_LOST_CONT BIT(18)
|
2020-07-14 15:34:49 +08:00
|
|
|
#define BR_MRP_LOST_IN_CONT BIT(19)
|
2014-11-28 21:34:22 +08:00
|
|
|
|
2015-09-23 23:39:16 +08:00
|
|
|
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
|
|
|
|
|
2007-09-18 02:56:21 +08:00
|
|
|
extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
|
2010-11-15 14:38:11 +08:00
|
|
|
|
2014-07-07 11:41:16 +08:00
|
|
|
#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
|
2014-06-08 00:26:28 +08:00
|
|
|
int br_multicast_list_adjacent(struct net_device *dev,
|
|
|
|
struct list_head *br_ip_list);
|
2014-07-07 11:41:17 +08:00
|
|
|
bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto);
|
2014-06-08 00:26:29 +08:00
|
|
|
bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto);
|
2021-05-13 21:20:53 +08:00
|
|
|
bool br_multicast_has_router_adjacent(struct net_device *dev, int proto);
|
2017-05-26 14:37:24 +08:00
|
|
|
bool br_multicast_enabled(const struct net_device *dev);
|
2017-10-09 17:15:32 +08:00
|
|
|
bool br_multicast_router(const struct net_device *dev);
|
net: bridge: add helper to replay port and host-joined mdb entries
I have a system with DSA ports, and udhcpcd is configured to bring
interfaces up as soon as they are created.
I create a bridge as follows:
ip link add br0 type bridge
As soon as I create the bridge and udhcpcd brings it up, I also have
avahi which automatically starts sending IPv6 packets to advertise some
local services, and because of that, the br0 bridge joins the following
IPv6 groups due to the code path detailed below:
33:33:ff:6d:c1:9c vid 0
33:33:00:00:00:6a vid 0
33:33:00:00:00:fb vid 0
br_dev_xmit
-> br_multicast_rcv
-> br_ip6_multicast_add_group
-> __br_multicast_add_group
-> br_multicast_host_join
-> br_mdb_notify
This is all fine, but inside br_mdb_notify we have br_mdb_switchdev_host
hooked up, and switchdev will attempt to offload the host joined groups
to an empty list of ports. Of course nobody offloads them.
Then when we add a port to br0:
ip link set swp0 master br0
the bridge doesn't replay the host-joined MDB entries from br_add_if,
and eventually the host joined addresses expire, and a switchdev
notification for deleting it is emitted, but surprise, the original
addition was already completely missed.
The strategy to address this problem is to replay the MDB entries (both
the port ones and the host joined ones) when the new port joins the
bridge, similar to what vxlan_fdb_replay does (in that case, its FDB can
be populated and only then attached to a bridge that you offload).
However there are 2 possibilities: the addresses can be 'pushed' by the
bridge into the port, or the port can 'pull' them from the bridge.
Considering that in the general case, the new port can be really late to
the party, and there may have been many other switchdev ports that
already received the initial notification, we would like to avoid
delivering duplicate events to them, since they might misbehave. And
currently, the bridge calls the entire switchdev notifier chain, whereas
for replaying it should just call the notifier block of the new guy.
But the bridge doesn't know what is the new guy's notifier block, it
just knows where the switchdev notifier chain is. So for simplification,
we make this a driver-initiated pull for now, and the notifier block is
passed as an argument.
To emulate the calling context for mdb objects (deferred and put on the
blocking notifier chain), we must iterate under RCU protection through
the bridge's mdb entries, queue them, and only call them once we're out
of the RCU read-side critical section.
There was some opportunity for reuse between br_mdb_switchdev_host_port,
br_mdb_notify and the newly added br_mdb_queue_one in how the switchdev
mdb object is created, so a helper was created.
Suggested-by: Ido Schimmel <idosch@idosch.org>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-23 07:51:44 +08:00
|
|
|
int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
|
2021-06-27 19:54:27 +08:00
|
|
|
const void *ctx, bool adding, struct notifier_block *nb,
|
2021-06-27 19:54:25 +08:00
|
|
|
struct netlink_ext_ack *extack);
|
2014-07-07 11:41:16 +08:00
|
|
|
#else
|
|
|
|
static inline int br_multicast_list_adjacent(struct net_device *dev,
|
|
|
|
struct list_head *br_ip_list)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2014-07-07 11:41:17 +08:00
|
|
|
static inline bool br_multicast_has_querier_anywhere(struct net_device *dev,
|
|
|
|
int proto)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2014-07-07 11:41:16 +08:00
|
|
|
static inline bool br_multicast_has_querier_adjacent(struct net_device *dev,
|
|
|
|
int proto)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2021-05-13 21:20:53 +08:00
|
|
|
|
|
|
|
static inline bool br_multicast_has_router_adjacent(struct net_device *dev,
|
|
|
|
int proto)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-26 14:37:24 +08:00
|
|
|
static inline bool br_multicast_enabled(const struct net_device *dev)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-09 17:15:32 +08:00
|
|
|
static inline bool br_multicast_router(const struct net_device *dev)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2021-06-27 19:54:26 +08:00
|
|
|
static inline int br_mdb_replay(const struct net_device *br_dev,
|
|
|
|
const struct net_device *dev, const void *ctx,
|
2021-06-27 19:54:27 +08:00
|
|
|
bool adding, struct notifier_block *nb,
|
net: bridge: add helper to replay port and host-joined mdb entries
I have a system with DSA ports, and udhcpcd is configured to bring
interfaces up as soon as they are created.
I create a bridge as follows:
ip link add br0 type bridge
As soon as I create the bridge and udhcpcd brings it up, I also have
avahi which automatically starts sending IPv6 packets to advertise some
local services, and because of that, the br0 bridge joins the following
IPv6 groups due to the code path detailed below:
33:33:ff:6d:c1:9c vid 0
33:33:00:00:00:6a vid 0
33:33:00:00:00:fb vid 0
br_dev_xmit
-> br_multicast_rcv
-> br_ip6_multicast_add_group
-> __br_multicast_add_group
-> br_multicast_host_join
-> br_mdb_notify
This is all fine, but inside br_mdb_notify we have br_mdb_switchdev_host
hooked up, and switchdev will attempt to offload the host joined groups
to an empty list of ports. Of course nobody offloads them.
Then when we add a port to br0:
ip link set swp0 master br0
the bridge doesn't replay the host-joined MDB entries from br_add_if,
and eventually the host joined addresses expire, and a switchdev
notification for deleting it is emitted, but surprise, the original
addition was already completely missed.
The strategy to address this problem is to replay the MDB entries (both
the port ones and the host joined ones) when the new port joins the
bridge, similar to what vxlan_fdb_replay does (in that case, its FDB can
be populated and only then attached to a bridge that you offload).
However there are 2 possibilities: the addresses can be 'pushed' by the
bridge into the port, or the port can 'pull' them from the bridge.
Considering that in the general case, the new port can be really late to
the party, and there may have been many other switchdev ports that
already received the initial notification, we would like to avoid
delivering duplicate events to them, since they might misbehave. And
currently, the bridge calls the entire switchdev notifier chain, whereas
for replaying it should just call the notifier block of the new guy.
But the bridge doesn't know what is the new guy's notifier block, it
just knows where the switchdev notifier chain is. So for simplification,
we make this a driver-initiated pull for now, and the notifier block is
passed as an argument.
To emulate the calling context for mdb objects (deferred and put on the
blocking notifier chain), we must iterate under RCU protection through
the bridge's mdb entries, queue them, and only call them once we're out
of the RCU read-side critical section.
There was some opportunity for reuse between br_mdb_switchdev_host_port,
br_mdb_notify and the newly added br_mdb_queue_one in how the switchdev
mdb object is created, so a helper was created.
Suggested-by: Ido Schimmel <idosch@idosch.org>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-23 07:51:44 +08:00
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
{
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2014-07-07 11:41:16 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-05-26 14:37:23 +08:00
|
|
|
#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING)
|
|
|
|
bool br_vlan_enabled(const struct net_device *dev);
|
2018-04-29 15:56:08 +08:00
|
|
|
int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid);
|
2019-07-05 21:16:34 +08:00
|
|
|
int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid);
|
2019-07-05 21:16:36 +08:00
|
|
|
int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto);
|
2018-04-29 15:56:08 +08:00
|
|
|
int br_vlan_get_info(const struct net_device *dev, u16 vid,
|
|
|
|
struct bridge_vlan_info *p_vinfo);
|
2021-03-23 07:51:46 +08:00
|
|
|
int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
|
2021-06-27 19:54:27 +08:00
|
|
|
const void *ctx, bool adding, struct notifier_block *nb,
|
2021-06-27 19:54:25 +08:00
|
|
|
struct netlink_ext_ack *extack);
|
2017-05-26 14:37:23 +08:00
|
|
|
#else
|
|
|
|
static inline bool br_vlan_enabled(const struct net_device *dev)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2018-04-29 15:56:08 +08:00
|
|
|
|
|
|
|
static inline int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
|
|
|
|
{
|
2018-07-06 20:44:02 +08:00
|
|
|
return -EINVAL;
|
2018-04-29 15:56:08 +08:00
|
|
|
}
|
|
|
|
|
2019-07-05 21:16:36 +08:00
|
|
|
static inline int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-07-05 21:16:34 +08:00
|
|
|
static inline int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-04-29 15:56:08 +08:00
|
|
|
static inline int br_vlan_get_info(const struct net_device *dev, u16 vid,
|
|
|
|
struct bridge_vlan_info *p_vinfo)
|
|
|
|
{
|
2018-07-06 20:44:02 +08:00
|
|
|
return -EINVAL;
|
2018-04-29 15:56:08 +08:00
|
|
|
}
|
2021-03-23 07:51:46 +08:00
|
|
|
|
|
|
|
static inline int br_vlan_replay(struct net_device *br_dev,
|
2021-06-27 19:54:25 +08:00
|
|
|
struct net_device *dev, const void *ctx,
|
2021-06-27 19:54:27 +08:00
|
|
|
bool adding, struct notifier_block *nb,
|
2021-03-23 07:51:46 +08:00
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
{
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2018-04-29 15:56:08 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_BRIDGE)
|
|
|
|
struct net_device *br_fdb_find_port(const struct net_device *br_dev,
|
|
|
|
const unsigned char *addr,
|
|
|
|
__u16 vid);
|
2018-12-08 03:55:07 +08:00
|
|
|
void br_fdb_clear_offload(const struct net_device *dev, u16 vid);
|
2018-11-21 16:02:41 +08:00
|
|
|
bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag);
|
2021-03-23 07:51:42 +08:00
|
|
|
u8 br_port_get_stp_state(const struct net_device *dev);
|
2021-06-27 19:54:26 +08:00
|
|
|
clock_t br_get_ageing_time(const struct net_device *br_dev);
|
|
|
|
int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev,
|
2021-06-27 19:54:27 +08:00
|
|
|
const void *ctx, bool adding, struct notifier_block *nb);
|
2018-04-29 15:56:08 +08:00
|
|
|
#else
|
|
|
|
static inline struct net_device *
|
|
|
|
br_fdb_find_port(const struct net_device *br_dev,
|
|
|
|
const unsigned char *addr,
|
|
|
|
__u16 vid)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-12-08 03:55:07 +08:00
|
|
|
|
|
|
|
static inline void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-11-21 16:02:41 +08:00
|
|
|
static inline bool
|
|
|
|
br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2021-03-23 07:51:42 +08:00
|
|
|
|
|
|
|
static inline u8 br_port_get_stp_state(const struct net_device *dev)
|
|
|
|
{
|
|
|
|
return BR_STATE_DISABLED;
|
|
|
|
}
|
2021-03-23 07:51:43 +08:00
|
|
|
|
2021-06-27 19:54:26 +08:00
|
|
|
static inline clock_t br_get_ageing_time(const struct net_device *br_dev)
|
2021-03-23 07:51:43 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
net: bridge: add helper to replay port and local fdb entries
When a switchdev port starts offloading a LAG that is already in a
bridge and has an FDB entry pointing to it:
ip link set bond0 master br0
bridge fdb add dev bond0 00:01:02:03:04:05 master static
ip link set swp0 master bond0
the switchdev driver will have no idea that this FDB entry is there,
because it missed the switchdev event emitted at its creation.
Ido Schimmel pointed this out during a discussion about challenges with
switchdev offloading of stacked interfaces between the physical port and
the bridge, and recommended to just catch that condition and deny the
CHANGEUPPER event:
https://lore.kernel.org/netdev/20210210105949.GB287766@shredder.lan/
But in fact, we might need to deal with the hard thing anyway, which is
to replay all FDB addresses relevant to this port, because it isn't just
static FDB entries, but also local addresses (ones that are not
forwarded but terminated by the bridge). There, we can't just say 'oh
yeah, there was an upper already so I'm not joining that'.
So, similar to the logic for replaying MDB entries, add a function that
must be called by individual switchdev drivers and replays local FDB
entries as well as ones pointing towards a bridge port. This time, we
use the atomic switchdev notifier block, since that's what FDB entries
expect for some reason.
Reported-by: Ido Schimmel <idosch@idosch.org>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-23 07:51:45 +08:00
|
|
|
|
2021-06-27 19:54:26 +08:00
|
|
|
static inline int br_fdb_replay(const struct net_device *br_dev,
|
|
|
|
const struct net_device *dev, const void *ctx,
|
2021-06-27 19:54:27 +08:00
|
|
|
bool adding, struct notifier_block *nb)
|
net: bridge: add helper to replay port and local fdb entries
When a switchdev port starts offloading a LAG that is already in a
bridge and has an FDB entry pointing to it:
ip link set bond0 master br0
bridge fdb add dev bond0 00:01:02:03:04:05 master static
ip link set swp0 master bond0
the switchdev driver will have no idea that this FDB entry is there,
because it missed the switchdev event emitted at its creation.
Ido Schimmel pointed this out during a discussion about challenges with
switchdev offloading of stacked interfaces between the physical port and
the bridge, and recommended to just catch that condition and deny the
CHANGEUPPER event:
https://lore.kernel.org/netdev/20210210105949.GB287766@shredder.lan/
But in fact, we might need to deal with the hard thing anyway, which is
to replay all FDB addresses relevant to this port, because it isn't just
static FDB entries, but also local addresses (ones that are not
forwarded but terminated by the bridge). There, we can't just say 'oh
yeah, there was an upper already so I'm not joining that'.
So, similar to the logic for replaying MDB entries, add a function that
must be called by individual switchdev drivers and replays local FDB
entries as well as ones pointing towards a bridge port. This time, we
use the atomic switchdev notifier block, since that's what FDB entries
expect for some reason.
Reported-by: Ido Schimmel <idosch@idosch.org>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-23 07:51:45 +08:00
|
|
|
{
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2017-05-26 14:37:23 +08:00
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|