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

bridge: Add multicast_router sysfs entries

This patch allows the user to forcibly enable/disable ports as
having multicast routers attached.  A port with a multicast router
will receive all multicast traffic.

The value 0 disables it completely.  The default is 1 which lets
the system automatically detect the presence of routers (currently
this is limited to picking up queries), and 2 means that the port
will always receive all multicast traffic.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c4fcb78c
...@@ -746,13 +746,31 @@ static int br_multicast_igmp3_report(struct net_bridge *br, ...@@ -746,13 +746,31 @@ static int br_multicast_igmp3_report(struct net_bridge *br,
return err; return err;
} }
static void br_multicast_mark_router(struct net_bridge *br, static void br_multicast_add_router(struct net_bridge *br,
struct net_bridge_port *port) struct net_bridge_port *port)
{ {
unsigned long now = jiffies;
struct hlist_node *p; struct hlist_node *p;
struct hlist_node **h; struct hlist_node **h;
for (h = &br->router_list.first;
(p = *h) &&
(unsigned long)container_of(p, struct net_bridge_port, rlist) >
(unsigned long)port;
h = &p->next)
;
port->rlist.pprev = h;
port->rlist.next = p;
rcu_assign_pointer(*h, &port->rlist);
if (p)
p->pprev = &port->rlist.next;
}
static void br_multicast_mark_router(struct net_bridge *br,
struct net_bridge_port *port)
{
unsigned long now = jiffies;
if (!port) { if (!port) {
if (br->multicast_router == 1) if (br->multicast_router == 1)
mod_timer(&br->multicast_router_timer, mod_timer(&br->multicast_router_timer,
...@@ -766,18 +784,7 @@ static void br_multicast_mark_router(struct net_bridge *br, ...@@ -766,18 +784,7 @@ static void br_multicast_mark_router(struct net_bridge *br,
if (!hlist_unhashed(&port->rlist)) if (!hlist_unhashed(&port->rlist))
goto timer; goto timer;
for (h = &br->router_list.first; br_multicast_add_router(br, port);
(p = *h) &&
(unsigned long)container_of(p, struct net_bridge_port, rlist) >
(unsigned long)port;
h = &p->next)
;
port->rlist.pprev = h;
port->rlist.next = p;
rcu_assign_pointer(*h, &port->rlist);
if (p)
p->pprev = &port->rlist.next;
timer: timer:
mod_timer(&port->multicast_router_timer, mod_timer(&port->multicast_router_timer,
...@@ -1133,3 +1140,73 @@ void br_multicast_stop(struct net_bridge *br) ...@@ -1133,3 +1140,73 @@ void br_multicast_stop(struct net_bridge *br)
out: out:
spin_unlock_bh(&br->multicast_lock); spin_unlock_bh(&br->multicast_lock);
} }
int br_multicast_set_router(struct net_bridge *br, unsigned long val)
{
int err = -ENOENT;
spin_lock_bh(&br->multicast_lock);
if (!netif_running(br->dev))
goto unlock;
switch (val) {
case 0:
case 2:
del_timer(&br->multicast_router_timer);
/* fall through */
case 1:
br->multicast_router = val;
err = 0;
break;
default:
err = -EINVAL;
break;
}
unlock:
spin_unlock_bh(&br->multicast_lock);
return err;
}
int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
{
struct net_bridge *br = p->br;
int err = -ENOENT;
spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) || p->state == BR_STATE_DISABLED)
goto unlock;
switch (val) {
case 0:
case 1:
case 2:
p->multicast_router = val;
err = 0;
if (val < 2 && !hlist_unhashed(&p->rlist))
hlist_del_init_rcu(&p->rlist);
if (val == 1)
break;
del_timer(&p->multicast_router_timer);
if (val == 0)
break;
br_multicast_add_router(br, p);
break;
default:
err = -EINVAL;
break;
}
unlock:
spin_unlock(&br->multicast_lock);
return err;
}
...@@ -297,6 +297,9 @@ extern void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, ...@@ -297,6 +297,9 @@ extern void br_multicast_deliver(struct net_bridge_mdb_entry *mdst,
struct sk_buff *skb); struct sk_buff *skb);
extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst, extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst,
struct sk_buff *skb, struct sk_buff *skb2); struct sk_buff *skb, struct sk_buff *skb2);
extern int br_multicast_set_router(struct net_bridge *br, unsigned long val);
extern int br_multicast_set_port_router(struct net_bridge_port *p,
unsigned long val);
#else #else
static inline int br_multicast_rcv(struct net_bridge *br, static inline int br_multicast_rcv(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
......
...@@ -345,6 +345,24 @@ static ssize_t store_flush(struct device *d, ...@@ -345,6 +345,24 @@ static ssize_t store_flush(struct device *d,
} }
static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush); static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct device *d,
struct device_attribute *attr, char *buf)
{
struct net_bridge *br = to_bridge(d);
return sprintf(buf, "%d\n", br->multicast_router);
}
static ssize_t store_multicast_router(struct device *d,
struct device_attribute *attr,
const char *buf, size_t len)
{
return store_bridge_parm(d, buf, len, br_multicast_set_router);
}
static DEVICE_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router,
store_multicast_router);
#endif
static struct attribute *bridge_attrs[] = { static struct attribute *bridge_attrs[] = {
&dev_attr_forward_delay.attr, &dev_attr_forward_delay.attr,
&dev_attr_hello_time.attr, &dev_attr_hello_time.attr,
...@@ -364,6 +382,9 @@ static struct attribute *bridge_attrs[] = { ...@@ -364,6 +382,9 @@ static struct attribute *bridge_attrs[] = {
&dev_attr_gc_timer.attr, &dev_attr_gc_timer.attr,
&dev_attr_group_addr.attr, &dev_attr_group_addr.attr,
&dev_attr_flush.attr, &dev_attr_flush.attr,
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
&dev_attr_multicast_router.attr,
#endif
NULL NULL
}; };
......
...@@ -159,6 +159,21 @@ static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v) ...@@ -159,6 +159,21 @@ static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v)
static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR, static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR,
show_hairpin_mode, store_hairpin_mode); show_hairpin_mode, store_hairpin_mode);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->multicast_router);
}
static ssize_t store_multicast_router(struct net_bridge_port *p,
unsigned long v)
{
return br_multicast_set_port_router(p, v);
}
static BRPORT_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router,
store_multicast_router);
#endif
static struct brport_attribute *brport_attrs[] = { static struct brport_attribute *brport_attrs[] = {
&brport_attr_path_cost, &brport_attr_path_cost,
&brport_attr_priority, &brport_attr_priority,
...@@ -176,6 +191,9 @@ static struct brport_attribute *brport_attrs[] = { ...@@ -176,6 +191,9 @@ static struct brport_attribute *brport_attrs[] = {
&brport_attr_hold_timer, &brport_attr_hold_timer,
&brport_attr_flush, &brport_attr_flush,
&brport_attr_hairpin_mode, &brport_attr_hairpin_mode,
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
&brport_attr_multicast_router,
#endif
NULL NULL
}; };
......
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