diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2010-02-27 14:41:49 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-28 03:49:45 -0500 |
commit | 0909e11758bd28848aeb6646e021ec1e031a3f0f (patch) | |
tree | a4ae57a59c6ff52f013bbfc29594715c383300d6 /net | |
parent | c4fcb78cf8ae55667809e54e54872a21025dd073 (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_multicast.c | 105 | ||||
-rw-r--r-- | net/bridge/br_private.h | 3 | ||||
-rw-r--r-- | net/bridge/br_sysfs_br.c | 21 | ||||
-rw-r--r-- | net/bridge/br_sysfs_if.c | 18 |
4 files changed, 133 insertions, 14 deletions
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 746b5a611aae..674224b6729d 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c | |||
@@ -746,12 +746,30 @@ static int br_multicast_igmp3_report(struct net_bridge *br, | |||
746 | return err; | 746 | return err; |
747 | } | 747 | } |
748 | 748 | ||
749 | static void br_multicast_add_router(struct net_bridge *br, | ||
750 | struct net_bridge_port *port) | ||
751 | { | ||
752 | struct hlist_node *p; | ||
753 | struct hlist_node **h; | ||
754 | |||
755 | for (h = &br->router_list.first; | ||
756 | (p = *h) && | ||
757 | (unsigned long)container_of(p, struct net_bridge_port, rlist) > | ||
758 | (unsigned long)port; | ||
759 | h = &p->next) | ||
760 | ; | ||
761 | |||
762 | port->rlist.pprev = h; | ||
763 | port->rlist.next = p; | ||
764 | rcu_assign_pointer(*h, &port->rlist); | ||
765 | if (p) | ||
766 | p->pprev = &port->rlist.next; | ||
767 | } | ||
768 | |||
749 | static void br_multicast_mark_router(struct net_bridge *br, | 769 | static void br_multicast_mark_router(struct net_bridge *br, |
750 | struct net_bridge_port *port) | 770 | struct net_bridge_port *port) |
751 | { | 771 | { |
752 | unsigned long now = jiffies; | 772 | unsigned long now = jiffies; |
753 | struct hlist_node *p; | ||
754 | struct hlist_node **h; | ||
755 | 773 | ||
756 | if (!port) { | 774 | if (!port) { |
757 | if (br->multicast_router == 1) | 775 | if (br->multicast_router == 1) |
@@ -766,18 +784,7 @@ static void br_multicast_mark_router(struct net_bridge *br, | |||
766 | if (!hlist_unhashed(&port->rlist)) | 784 | if (!hlist_unhashed(&port->rlist)) |
767 | goto timer; | 785 | goto timer; |
768 | 786 | ||
769 | for (h = &br->router_list.first; | 787 | br_multicast_add_router(br, port); |
770 | (p = *h) && | ||
771 | (unsigned long)container_of(p, struct net_bridge_port, rlist) > | ||
772 | (unsigned long)port; | ||
773 | h = &p->next) | ||
774 | ; | ||
775 | |||
776 | port->rlist.pprev = h; | ||
777 | port->rlist.next = p; | ||
778 | rcu_assign_pointer(*h, &port->rlist); | ||
779 | if (p) | ||
780 | p->pprev = &port->rlist.next; | ||
781 | 788 | ||
782 | timer: | 789 | timer: |
783 | mod_timer(&port->multicast_router_timer, | 790 | mod_timer(&port->multicast_router_timer, |
@@ -1133,3 +1140,73 @@ void br_multicast_stop(struct net_bridge *br) | |||
1133 | out: | 1140 | out: |
1134 | spin_unlock_bh(&br->multicast_lock); | 1141 | spin_unlock_bh(&br->multicast_lock); |
1135 | } | 1142 | } |
1143 | |||
1144 | int br_multicast_set_router(struct net_bridge *br, unsigned long val) | ||
1145 | { | ||
1146 | int err = -ENOENT; | ||
1147 | |||
1148 | spin_lock_bh(&br->multicast_lock); | ||
1149 | if (!netif_running(br->dev)) | ||
1150 | goto unlock; | ||
1151 | |||
1152 | switch (val) { | ||
1153 | case 0: | ||
1154 | case 2: | ||
1155 | del_timer(&br->multicast_router_timer); | ||
1156 | /* fall through */ | ||
1157 | case 1: | ||
1158 | br->multicast_router = val; | ||
1159 | err = 0; | ||
1160 | break; | ||
1161 | |||
1162 | default: | ||
1163 | err = -EINVAL; | ||
1164 | break; | ||
1165 | } | ||
1166 | |||
1167 | unlock: | ||
1168 | spin_unlock_bh(&br->multicast_lock); | ||
1169 | |||
1170 | return err; | ||
1171 | } | ||
1172 | |||
1173 | int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val) | ||
1174 | { | ||
1175 | struct net_bridge *br = p->br; | ||
1176 | int err = -ENOENT; | ||
1177 | |||
1178 | spin_lock(&br->multicast_lock); | ||
1179 | if (!netif_running(br->dev) || p->state == BR_STATE_DISABLED) | ||
1180 | goto unlock; | ||
1181 | |||
1182 | switch (val) { | ||
1183 | case 0: | ||
1184 | case 1: | ||
1185 | case 2: | ||
1186 | p->multicast_router = val; | ||
1187 | err = 0; | ||
1188 | |||
1189 | if (val < 2 && !hlist_unhashed(&p->rlist)) | ||
1190 | hlist_del_init_rcu(&p->rlist); | ||
1191 | |||
1192 | if (val == 1) | ||
1193 | break; | ||
1194 | |||
1195 | del_timer(&p->multicast_router_timer); | ||
1196 | |||
1197 | if (val == 0) | ||
1198 | break; | ||
1199 | |||
1200 | br_multicast_add_router(br, p); | ||
1201 | break; | ||
1202 | |||
1203 | default: | ||
1204 | err = -EINVAL; | ||
1205 | break; | ||
1206 | } | ||
1207 | |||
1208 | unlock: | ||
1209 | spin_unlock(&br->multicast_lock); | ||
1210 | |||
1211 | return err; | ||
1212 | } | ||
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index c85943c2b23f..dcdfafbe4b17 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -297,6 +297,9 @@ extern void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, | |||
297 | struct sk_buff *skb); | 297 | struct sk_buff *skb); |
298 | extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst, | 298 | extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst, |
299 | struct sk_buff *skb, struct sk_buff *skb2); | 299 | struct sk_buff *skb, struct sk_buff *skb2); |
300 | extern int br_multicast_set_router(struct net_bridge *br, unsigned long val); | ||
301 | extern int br_multicast_set_port_router(struct net_bridge_port *p, | ||
302 | unsigned long val); | ||
300 | #else | 303 | #else |
301 | static inline int br_multicast_rcv(struct net_bridge *br, | 304 | static inline int br_multicast_rcv(struct net_bridge *br, |
302 | struct net_bridge_port *port, | 305 | struct net_bridge_port *port, |
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index bee4f300d0c8..cb742016db21 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c | |||
@@ -345,6 +345,24 @@ static ssize_t store_flush(struct device *d, | |||
345 | } | 345 | } |
346 | static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush); | 346 | static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush); |
347 | 347 | ||
348 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||
349 | static ssize_t show_multicast_router(struct device *d, | ||
350 | struct device_attribute *attr, char *buf) | ||
351 | { | ||
352 | struct net_bridge *br = to_bridge(d); | ||
353 | return sprintf(buf, "%d\n", br->multicast_router); | ||
354 | } | ||
355 | |||
356 | static ssize_t store_multicast_router(struct device *d, | ||
357 | struct device_attribute *attr, | ||
358 | const char *buf, size_t len) | ||
359 | { | ||
360 | return store_bridge_parm(d, buf, len, br_multicast_set_router); | ||
361 | } | ||
362 | static DEVICE_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router, | ||
363 | store_multicast_router); | ||
364 | #endif | ||
365 | |||
348 | static struct attribute *bridge_attrs[] = { | 366 | static struct attribute *bridge_attrs[] = { |
349 | &dev_attr_forward_delay.attr, | 367 | &dev_attr_forward_delay.attr, |
350 | &dev_attr_hello_time.attr, | 368 | &dev_attr_hello_time.attr, |
@@ -364,6 +382,9 @@ static struct attribute *bridge_attrs[] = { | |||
364 | &dev_attr_gc_timer.attr, | 382 | &dev_attr_gc_timer.attr, |
365 | &dev_attr_group_addr.attr, | 383 | &dev_attr_group_addr.attr, |
366 | &dev_attr_flush.attr, | 384 | &dev_attr_flush.attr, |
385 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||
386 | &dev_attr_multicast_router.attr, | ||
387 | #endif | ||
367 | NULL | 388 | NULL |
368 | }; | 389 | }; |
369 | 390 | ||
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 820643a3ba9c..696596cd3384 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c | |||
@@ -159,6 +159,21 @@ static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v) | |||
159 | static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR, | 159 | static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR, |
160 | show_hairpin_mode, store_hairpin_mode); | 160 | show_hairpin_mode, store_hairpin_mode); |
161 | 161 | ||
162 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||
163 | static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) | ||
164 | { | ||
165 | return sprintf(buf, "%d\n", p->multicast_router); | ||
166 | } | ||
167 | |||
168 | static ssize_t store_multicast_router(struct net_bridge_port *p, | ||
169 | unsigned long v) | ||
170 | { | ||
171 | return br_multicast_set_port_router(p, v); | ||
172 | } | ||
173 | static BRPORT_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router, | ||
174 | store_multicast_router); | ||
175 | #endif | ||
176 | |||
162 | static struct brport_attribute *brport_attrs[] = { | 177 | static struct brport_attribute *brport_attrs[] = { |
163 | &brport_attr_path_cost, | 178 | &brport_attr_path_cost, |
164 | &brport_attr_priority, | 179 | &brport_attr_priority, |
@@ -176,6 +191,9 @@ static struct brport_attribute *brport_attrs[] = { | |||
176 | &brport_attr_hold_timer, | 191 | &brport_attr_hold_timer, |
177 | &brport_attr_flush, | 192 | &brport_attr_flush, |
178 | &brport_attr_hairpin_mode, | 193 | &brport_attr_hairpin_mode, |
194 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||
195 | &brport_attr_multicast_router, | ||
196 | #endif | ||
179 | NULL | 197 | NULL |
180 | }; | 198 | }; |
181 | 199 | ||