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: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									c4fcb78cf8
								
							
						
					
					
						commit
						0909e11758
					
				| @ -746,12 +746,30 @@ static int br_multicast_igmp3_report(struct net_bridge *br, | |||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void br_multicast_add_router(struct net_bridge *br, | ||||||
|  | 				    struct net_bridge_port *port) | ||||||
|  | { | ||||||
|  | 	struct hlist_node *p; | ||||||
|  | 	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, | static void br_multicast_mark_router(struct net_bridge *br, | ||||||
| 				     struct net_bridge_port *port) | 				     struct net_bridge_port *port) | ||||||
| { | { | ||||||
| 	unsigned long now = jiffies; | 	unsigned long now = jiffies; | ||||||
| 	struct hlist_node *p; |  | ||||||
| 	struct hlist_node **h; |  | ||||||
| 
 | 
 | ||||||
| 	if (!port) { | 	if (!port) { | ||||||
| 		if (br->multicast_router == 1) | 		if (br->multicast_router == 1) | ||||||
| @ -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) | |||||||
| 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, | |||||||
| 				 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, | |||||||
| } | } | ||||||
| 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[] = { | |||||||
| 	&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) | |||||||
| 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[] = { | |||||||
| 	&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 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user