Commit eb1d1641 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

bridge: Add core IGMP snooping support

This patch adds the core functionality of IGMP snooping support
without actually hooking it up.  So this patch should be a no-op
as far as the bridge's external behaviour is concerned.

All the new code and data is controlled by the Kconfig option
BRIDGE_IGMP_SNOOPING.  A run-time toggle is also available.

The multicast switching is done using an hash table that is
lockless on the read-side through RCU.  On the write-side the
new multicast_lock is used for all operations.  The hash table
supports dynamic growth/rehashing.

The hash table will be rehashed if any chain length exceeds a
preset limit.  If rehashing does not reduce the maximum chain
length then snooping will be disabled.

These features may be added in future (in no particular order):

* IGMPv3 source support
* Non-querier router detection
* IPv6
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 025d89c2
...@@ -31,3 +31,15 @@ config BRIDGE ...@@ -31,3 +31,15 @@ config BRIDGE
will be called bridge. will be called bridge.
If unsure, say N. If unsure, say N.
config BRIDGE_IGMP_SNOOPING
bool "IGMP snooping"
default y
---help---
If you say Y here, then the Ethernet bridge will be able selectively
forward multicast traffic based on IGMP traffic received from each
port.
Say N to exclude this support and reduce the binary size.
If unsure, say Y.
...@@ -12,4 +12,6 @@ bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o ...@@ -12,4 +12,6 @@ bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
This diff is collapsed.
...@@ -57,6 +57,41 @@ struct net_bridge_fdb_entry ...@@ -57,6 +57,41 @@ struct net_bridge_fdb_entry
unsigned char is_static; unsigned char is_static;
}; };
struct net_bridge_port_group {
struct net_bridge_port *port;
struct net_bridge_port_group *next;
struct hlist_node mglist;
struct rcu_head rcu;
struct timer_list timer;
struct timer_list query_timer;
__be32 addr;
u32 queries_sent;
};
struct net_bridge_mdb_entry
{
struct hlist_node hlist[2];
struct hlist_node mglist;
struct net_bridge *br;
struct net_bridge_port_group *ports;
struct rcu_head rcu;
struct timer_list timer;
struct timer_list query_timer;
__be32 addr;
u32 queries_sent;
};
struct net_bridge_mdb_htable
{
struct hlist_head *mhash;
struct rcu_head rcu;
struct net_bridge_mdb_htable *old;
u32 size;
u32 max;
u32 secret;
u32 ver;
};
struct net_bridge_port struct net_bridge_port
{ {
struct net_bridge *br; struct net_bridge *br;
...@@ -84,6 +119,15 @@ struct net_bridge_port ...@@ -84,6 +119,15 @@ struct net_bridge_port
unsigned long flags; unsigned long flags;
#define BR_HAIRPIN_MODE 0x00000001 #define BR_HAIRPIN_MODE 0x00000001
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
u32 multicast_startup_queries_sent;
unsigned char multicast_router;
struct timer_list multicast_router_timer;
struct timer_list multicast_query_timer;
struct hlist_head mglist;
struct hlist_node rlist;
#endif
}; };
struct net_bridge struct net_bridge
...@@ -124,6 +168,35 @@ struct net_bridge ...@@ -124,6 +168,35 @@ struct net_bridge
unsigned char topology_change; unsigned char topology_change;
unsigned char topology_change_detected; unsigned char topology_change_detected;
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
unsigned char multicast_router;
u8 multicast_disabled:1;
u32 hash_elasticity;
u32 hash_max;
u32 multicast_last_member_count;
u32 multicast_startup_queries_sent;
u32 multicast_startup_query_count;
unsigned long multicast_last_member_interval;
unsigned long multicast_membership_interval;
unsigned long multicast_querier_interval;
unsigned long multicast_query_interval;
unsigned long multicast_query_response_interval;
unsigned long multicast_startup_query_interval;
spinlock_t multicast_lock;
struct net_bridge_mdb_htable *mdb;
struct hlist_head router_list;
struct hlist_head mglist;
struct timer_list multicast_router_timer;
struct timer_list multicast_querier_timer;
struct timer_list multicast_query_timer;
#endif
struct timer_list hello_timer; struct timer_list hello_timer;
struct timer_list tcn_timer; struct timer_list tcn_timer;
struct timer_list topology_change_timer; struct timer_list topology_change_timer;
...@@ -133,6 +206,8 @@ struct net_bridge ...@@ -133,6 +206,8 @@ struct net_bridge
struct br_input_skb_cb { struct br_input_skb_cb {
struct net_device *brdev; struct net_device *brdev;
int igmp;
int mrouters_only;
}; };
#define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb) #define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb)
...@@ -204,6 +279,70 @@ extern struct sk_buff *br_handle_frame(struct net_bridge_port *p, ...@@ -204,6 +279,70 @@ extern struct sk_buff *br_handle_frame(struct net_bridge_port *p,
extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg); extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg);
/* br_multicast.c */
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
extern int br_multicast_rcv(struct net_bridge *br,
struct net_bridge_port *port,
struct sk_buff *skb);
extern struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
struct sk_buff *skb);
extern void br_multicast_add_port(struct net_bridge_port *port);
extern void br_multicast_del_port(struct net_bridge_port *port);
extern void br_multicast_enable_port(struct net_bridge_port *port);
extern void br_multicast_disable_port(struct net_bridge_port *port);
extern void br_multicast_init(struct net_bridge *br);
extern void br_multicast_open(struct net_bridge *br);
extern void br_multicast_stop(struct net_bridge *br);
#else
static inline int br_multicast_rcv(struct net_bridge *br,
struct net_bridge_port *port,
struct sk_buff *skb)
{
return 0;
}
static inline struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
struct sk_buff *skb)
{
return NULL;
}
static inline void br_multicast_add_port(struct net_bridge_port *port)
{
}
static inline void br_multicast_del_port(struct net_bridge_port *port)
{
}
static inline void br_multicast_enable_port(struct net_bridge_port *port)
{
}
static inline void br_multicast_disable_port(struct net_bridge_port *port)
{
}
static inline void br_multicast_init(struct net_bridge *br)
{
}
static inline void br_multicast_open(struct net_bridge *br)
{
}
static inline void br_multicast_stop(struct net_bridge *br)
{
}
#endif
static inline bool br_multicast_is_router(struct net_bridge *br)
{
return br->multicast_router == 2 ||
(br->multicast_router == 1 &&
timer_pending(&br->multicast_router_timer));
}
/* br_netfilter.c */ /* br_netfilter.c */
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
extern int br_netfilter_init(void); extern int br_netfilter_init(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment