diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2010-02-27 14:41:45 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-28 03:48:45 -0500 |
commit | eb1d16414339a6e113d89e2cca2556005d7ce919 (patch) | |
tree | b6c49f870cc36241a894a8c723a38e224d41464b /net | |
parent | 025d89c27f54c69cd0e51666d88aada33666bb6c (diff) |
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: 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/Kconfig | 12 | ||||
-rw-r--r-- | net/bridge/Makefile | 2 | ||||
-rw-r--r-- | net/bridge/br_multicast.c | 1135 | ||||
-rw-r--r-- | net/bridge/br_private.h | 139 |
4 files changed, 1288 insertions, 0 deletions
diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index e143ca678881..78dd5497210a 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig | |||
@@ -31,3 +31,15 @@ config BRIDGE | |||
31 | will be called bridge. | 31 | will be called bridge. |
32 | 32 | ||
33 | If unsure, say N. | 33 | If unsure, say N. |
34 | |||
35 | config BRIDGE_IGMP_SNOOPING | ||
36 | bool "IGMP snooping" | ||
37 | default y | ||
38 | ---help--- | ||
39 | If you say Y here, then the Ethernet bridge will be able selectively | ||
40 | forward multicast traffic based on IGMP traffic received from each | ||
41 | port. | ||
42 | |||
43 | Say N to exclude this support and reduce the binary size. | ||
44 | |||
45 | If unsure, say Y. | ||
diff --git a/net/bridge/Makefile b/net/bridge/Makefile index f444c12cde5a..d0359ea8ee79 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile | |||
@@ -12,4 +12,6 @@ bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o | |||
12 | 12 | ||
13 | bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o | 13 | bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o |
14 | 14 | ||
15 | bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o | ||
16 | |||
15 | obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ | 17 | obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ |
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c new file mode 100644 index 000000000000..746b5a611aae --- /dev/null +++ b/net/bridge/br_multicast.c | |||
@@ -0,0 +1,1135 @@ | |||
1 | /* | ||
2 | * Bridge multicast support. | ||
3 | * | ||
4 | * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the Free | ||
8 | * Software Foundation; either version 2 of the License, or (at your option) | ||
9 | * any later version. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/err.h> | ||
14 | #include <linux/if_ether.h> | ||
15 | #include <linux/igmp.h> | ||
16 | #include <linux/jhash.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/netfilter_bridge.h> | ||
20 | #include <linux/random.h> | ||
21 | #include <linux/rculist.h> | ||
22 | #include <linux/skbuff.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/timer.h> | ||
25 | #include <net/ip.h> | ||
26 | |||
27 | #include "br_private.h" | ||
28 | |||
29 | static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb, __be32 ip) | ||
30 | { | ||
31 | return jhash_1word(mdb->secret, (u32)ip) & (mdb->max - 1); | ||
32 | } | ||
33 | |||
34 | static struct net_bridge_mdb_entry *__br_mdb_ip_get( | ||
35 | struct net_bridge_mdb_htable *mdb, __be32 dst, int hash) | ||
36 | { | ||
37 | struct net_bridge_mdb_entry *mp; | ||
38 | struct hlist_node *p; | ||
39 | |||
40 | hlist_for_each_entry(mp, p, &mdb->mhash[hash], hlist[mdb->ver]) { | ||
41 | if (dst == mp->addr) | ||
42 | return mp; | ||
43 | } | ||
44 | |||
45 | return NULL; | ||
46 | } | ||
47 | |||
48 | static struct net_bridge_mdb_entry *br_mdb_ip_get( | ||
49 | struct net_bridge_mdb_htable *mdb, __be32 dst) | ||
50 | { | ||
51 | return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst)); | ||
52 | } | ||
53 | |||
54 | struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, | ||
55 | struct sk_buff *skb) | ||
56 | { | ||
57 | struct net_bridge_mdb_htable *mdb = br->mdb; | ||
58 | |||
59 | if (!mdb || br->multicast_disabled) | ||
60 | return NULL; | ||
61 | |||
62 | switch (skb->protocol) { | ||
63 | case htons(ETH_P_IP): | ||
64 | if (BR_INPUT_SKB_CB(skb)->igmp) | ||
65 | break; | ||
66 | return br_mdb_ip_get(mdb, ip_hdr(skb)->daddr); | ||
67 | } | ||
68 | |||
69 | return NULL; | ||
70 | } | ||
71 | |||
72 | static void br_mdb_free(struct rcu_head *head) | ||
73 | { | ||
74 | struct net_bridge_mdb_htable *mdb = | ||
75 | container_of(head, struct net_bridge_mdb_htable, rcu); | ||
76 | struct net_bridge_mdb_htable *old = mdb->old; | ||
77 | |||
78 | mdb->old = NULL; | ||
79 | kfree(old->mhash); | ||
80 | kfree(old); | ||
81 | } | ||
82 | |||
83 | static int br_mdb_copy(struct net_bridge_mdb_htable *new, | ||
84 | struct net_bridge_mdb_htable *old, | ||
85 | int elasticity) | ||
86 | { | ||
87 | struct net_bridge_mdb_entry *mp; | ||
88 | struct hlist_node *p; | ||
89 | int maxlen; | ||
90 | int len; | ||
91 | int i; | ||
92 | |||
93 | for (i = 0; i < old->max; i++) | ||
94 | hlist_for_each_entry(mp, p, &old->mhash[i], hlist[old->ver]) | ||
95 | hlist_add_head(&mp->hlist[new->ver], | ||
96 | &new->mhash[br_ip_hash(new, mp->addr)]); | ||
97 | |||
98 | if (!elasticity) | ||
99 | return 0; | ||
100 | |||
101 | maxlen = 0; | ||
102 | for (i = 0; i < new->max; i++) { | ||
103 | len = 0; | ||
104 | hlist_for_each_entry(mp, p, &new->mhash[i], hlist[new->ver]) | ||
105 | len++; | ||
106 | if (len > maxlen) | ||
107 | maxlen = len; | ||
108 | } | ||
109 | |||
110 | return maxlen > elasticity ? -EINVAL : 0; | ||
111 | } | ||
112 | |||
113 | static void br_multicast_free_pg(struct rcu_head *head) | ||
114 | { | ||
115 | struct net_bridge_port_group *p = | ||
116 | container_of(head, struct net_bridge_port_group, rcu); | ||
117 | |||
118 | kfree(p); | ||
119 | } | ||
120 | |||
121 | static void br_multicast_free_group(struct rcu_head *head) | ||
122 | { | ||
123 | struct net_bridge_mdb_entry *mp = | ||
124 | container_of(head, struct net_bridge_mdb_entry, rcu); | ||
125 | |||
126 | kfree(mp); | ||
127 | } | ||
128 | |||
129 | static void br_multicast_group_expired(unsigned long data) | ||
130 | { | ||
131 | struct net_bridge_mdb_entry *mp = (void *)data; | ||
132 | struct net_bridge *br = mp->br; | ||
133 | struct net_bridge_mdb_htable *mdb; | ||
134 | |||
135 | spin_lock(&br->multicast_lock); | ||
136 | if (!netif_running(br->dev) || timer_pending(&mp->timer)) | ||
137 | goto out; | ||
138 | |||
139 | if (!hlist_unhashed(&mp->mglist)) | ||
140 | hlist_del_init(&mp->mglist); | ||
141 | |||
142 | if (mp->ports) | ||
143 | goto out; | ||
144 | |||
145 | mdb = br->mdb; | ||
146 | hlist_del_rcu(&mp->hlist[mdb->ver]); | ||
147 | mdb->size--; | ||
148 | |||
149 | del_timer(&mp->query_timer); | ||
150 | call_rcu_bh(&mp->rcu, br_multicast_free_group); | ||
151 | |||
152 | out: | ||
153 | spin_unlock(&br->multicast_lock); | ||
154 | } | ||
155 | |||
156 | static void br_multicast_del_pg(struct net_bridge *br, | ||
157 | struct net_bridge_port_group *pg) | ||
158 | { | ||
159 | struct net_bridge_mdb_htable *mdb = br->mdb; | ||
160 | struct net_bridge_mdb_entry *mp; | ||
161 | struct net_bridge_port_group *p; | ||
162 | struct net_bridge_port_group **pp; | ||
163 | |||
164 | mp = br_mdb_ip_get(mdb, pg->addr); | ||
165 | if (WARN_ON(!mp)) | ||
166 | return; | ||
167 | |||
168 | for (pp = &mp->ports; (p = *pp); pp = &p->next) { | ||
169 | if (p != pg) | ||
170 | continue; | ||
171 | |||
172 | *pp = p->next; | ||
173 | hlist_del_init(&p->mglist); | ||
174 | del_timer(&p->timer); | ||
175 | del_timer(&p->query_timer); | ||
176 | call_rcu_bh(&p->rcu, br_multicast_free_pg); | ||
177 | |||
178 | if (!mp->ports && hlist_unhashed(&mp->mglist) && | ||
179 | netif_running(br->dev)) | ||
180 | mod_timer(&mp->timer, jiffies); | ||
181 | |||
182 | return; | ||
183 | } | ||
184 | |||
185 | WARN_ON(1); | ||
186 | } | ||
187 | |||
188 | static void br_multicast_port_group_expired(unsigned long data) | ||
189 | { | ||
190 | struct net_bridge_port_group *pg = (void *)data; | ||
191 | struct net_bridge *br = pg->port->br; | ||
192 | |||
193 | spin_lock(&br->multicast_lock); | ||
194 | if (!netif_running(br->dev) || timer_pending(&pg->timer) || | ||
195 | hlist_unhashed(&pg->mglist)) | ||
196 | goto out; | ||
197 | |||
198 | br_multicast_del_pg(br, pg); | ||
199 | |||
200 | out: | ||
201 | spin_unlock(&br->multicast_lock); | ||
202 | } | ||
203 | |||
204 | static int br_mdb_rehash(struct net_bridge_mdb_htable **mdbp, int max, | ||
205 | int elasticity) | ||
206 | { | ||
207 | struct net_bridge_mdb_htable *old = *mdbp; | ||
208 | struct net_bridge_mdb_htable *mdb; | ||
209 | int err; | ||
210 | |||
211 | mdb = kmalloc(sizeof(*mdb), GFP_ATOMIC); | ||
212 | if (!mdb) | ||
213 | return -ENOMEM; | ||
214 | |||
215 | mdb->max = max; | ||
216 | mdb->old = old; | ||
217 | |||
218 | mdb->mhash = kzalloc(max * sizeof(*mdb->mhash), GFP_ATOMIC); | ||
219 | if (!mdb->mhash) { | ||
220 | kfree(mdb); | ||
221 | return -ENOMEM; | ||
222 | } | ||
223 | |||
224 | mdb->size = old ? old->size : 0; | ||
225 | mdb->ver = old ? old->ver ^ 1 : 0; | ||
226 | |||
227 | if (!old || elasticity) | ||
228 | get_random_bytes(&mdb->secret, sizeof(mdb->secret)); | ||
229 | else | ||
230 | mdb->secret = old->secret; | ||
231 | |||
232 | if (!old) | ||
233 | goto out; | ||
234 | |||
235 | err = br_mdb_copy(mdb, old, elasticity); | ||
236 | if (err) { | ||
237 | kfree(mdb->mhash); | ||
238 | kfree(mdb); | ||
239 | return err; | ||
240 | } | ||
241 | |||
242 | call_rcu_bh(&mdb->rcu, br_mdb_free); | ||
243 | |||
244 | out: | ||
245 | rcu_assign_pointer(*mdbp, mdb); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br, | ||
251 | __be32 group) | ||
252 | { | ||
253 | struct sk_buff *skb; | ||
254 | struct igmphdr *ih; | ||
255 | struct ethhdr *eth; | ||
256 | struct iphdr *iph; | ||
257 | |||
258 | skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*eth) + sizeof(*iph) + | ||
259 | sizeof(*ih) + 4); | ||
260 | if (!skb) | ||
261 | goto out; | ||
262 | |||
263 | skb->protocol = htons(ETH_P_IP); | ||
264 | |||
265 | skb_reset_mac_header(skb); | ||
266 | eth = eth_hdr(skb); | ||
267 | |||
268 | memcpy(eth->h_source, br->dev->dev_addr, 6); | ||
269 | eth->h_dest[0] = 1; | ||
270 | eth->h_dest[1] = 0; | ||
271 | eth->h_dest[2] = 0x5e; | ||
272 | eth->h_dest[3] = 0; | ||
273 | eth->h_dest[4] = 0; | ||
274 | eth->h_dest[5] = 1; | ||
275 | eth->h_proto = htons(ETH_P_IP); | ||
276 | skb_put(skb, sizeof(*eth)); | ||
277 | |||
278 | skb_set_network_header(skb, skb->len); | ||
279 | iph = ip_hdr(skb); | ||
280 | |||
281 | iph->version = 4; | ||
282 | iph->ihl = 6; | ||
283 | iph->tos = 0xc0; | ||
284 | iph->tot_len = htons(sizeof(*iph) + sizeof(*ih) + 4); | ||
285 | iph->id = 0; | ||
286 | iph->frag_off = htons(IP_DF); | ||
287 | iph->ttl = 1; | ||
288 | iph->protocol = IPPROTO_IGMP; | ||
289 | iph->saddr = 0; | ||
290 | iph->daddr = htonl(INADDR_ALLHOSTS_GROUP); | ||
291 | ((u8 *)&iph[1])[0] = IPOPT_RA; | ||
292 | ((u8 *)&iph[1])[1] = 4; | ||
293 | ((u8 *)&iph[1])[2] = 0; | ||
294 | ((u8 *)&iph[1])[3] = 0; | ||
295 | ip_send_check(iph); | ||
296 | skb_put(skb, 24); | ||
297 | |||
298 | skb_set_transport_header(skb, skb->len); | ||
299 | ih = igmp_hdr(skb); | ||
300 | ih->type = IGMP_HOST_MEMBERSHIP_QUERY; | ||
301 | ih->code = (group ? br->multicast_last_member_interval : | ||
302 | br->multicast_query_response_interval) / | ||
303 | (HZ / IGMP_TIMER_SCALE); | ||
304 | ih->group = group; | ||
305 | ih->csum = 0; | ||
306 | ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); | ||
307 | skb_put(skb, sizeof(*ih)); | ||
308 | |||
309 | __skb_pull(skb, sizeof(*eth)); | ||
310 | |||
311 | out: | ||
312 | return skb; | ||
313 | } | ||
314 | |||
315 | static void br_multicast_send_group_query(struct net_bridge_mdb_entry *mp) | ||
316 | { | ||
317 | struct net_bridge *br = mp->br; | ||
318 | struct sk_buff *skb; | ||
319 | |||
320 | skb = br_multicast_alloc_query(br, mp->addr); | ||
321 | if (!skb) | ||
322 | goto timer; | ||
323 | |||
324 | netif_rx(skb); | ||
325 | |||
326 | timer: | ||
327 | if (++mp->queries_sent < br->multicast_last_member_count) | ||
328 | mod_timer(&mp->query_timer, | ||
329 | jiffies + br->multicast_last_member_interval); | ||
330 | } | ||
331 | |||
332 | static void br_multicast_group_query_expired(unsigned long data) | ||
333 | { | ||
334 | struct net_bridge_mdb_entry *mp = (void *)data; | ||
335 | struct net_bridge *br = mp->br; | ||
336 | |||
337 | spin_lock(&br->multicast_lock); | ||
338 | if (!netif_running(br->dev) || hlist_unhashed(&mp->mglist) || | ||
339 | mp->queries_sent >= br->multicast_last_member_count) | ||
340 | goto out; | ||
341 | |||
342 | br_multicast_send_group_query(mp); | ||
343 | |||
344 | out: | ||
345 | spin_unlock(&br->multicast_lock); | ||
346 | } | ||
347 | |||
348 | static void br_multicast_send_port_group_query(struct net_bridge_port_group *pg) | ||
349 | { | ||
350 | struct net_bridge_port *port = pg->port; | ||
351 | struct net_bridge *br = port->br; | ||
352 | struct sk_buff *skb; | ||
353 | |||
354 | skb = br_multicast_alloc_query(br, pg->addr); | ||
355 | if (!skb) | ||
356 | goto timer; | ||
357 | |||
358 | br_deliver(port, skb); | ||
359 | |||
360 | timer: | ||
361 | if (++pg->queries_sent < br->multicast_last_member_count) | ||
362 | mod_timer(&pg->query_timer, | ||
363 | jiffies + br->multicast_last_member_interval); | ||
364 | } | ||
365 | |||
366 | static void br_multicast_port_group_query_expired(unsigned long data) | ||
367 | { | ||
368 | struct net_bridge_port_group *pg = (void *)data; | ||
369 | struct net_bridge_port *port = pg->port; | ||
370 | struct net_bridge *br = port->br; | ||
371 | |||
372 | spin_lock(&br->multicast_lock); | ||
373 | if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) || | ||
374 | pg->queries_sent >= br->multicast_last_member_count) | ||
375 | goto out; | ||
376 | |||
377 | br_multicast_send_port_group_query(pg); | ||
378 | |||
379 | out: | ||
380 | spin_unlock(&br->multicast_lock); | ||
381 | } | ||
382 | |||
383 | static struct net_bridge_mdb_entry *br_multicast_get_group( | ||
384 | struct net_bridge *br, struct net_bridge_port *port, __be32 group, | ||
385 | int hash) | ||
386 | { | ||
387 | struct net_bridge_mdb_htable *mdb = br->mdb; | ||
388 | struct net_bridge_mdb_entry *mp; | ||
389 | struct hlist_node *p; | ||
390 | unsigned count = 0; | ||
391 | unsigned max; | ||
392 | int elasticity; | ||
393 | int err; | ||
394 | |||
395 | hlist_for_each_entry(mp, p, &mdb->mhash[hash], hlist[mdb->ver]) { | ||
396 | count++; | ||
397 | if (unlikely(group == mp->addr)) { | ||
398 | return mp; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | elasticity = 0; | ||
403 | max = mdb->max; | ||
404 | |||
405 | if (unlikely(count > br->hash_elasticity && count)) { | ||
406 | if (net_ratelimit()) | ||
407 | printk(KERN_INFO "%s: Multicast hash table " | ||
408 | "chain limit reached: %s\n", | ||
409 | br->dev->name, port ? port->dev->name : | ||
410 | br->dev->name); | ||
411 | |||
412 | elasticity = br->hash_elasticity; | ||
413 | } | ||
414 | |||
415 | if (mdb->size >= max) { | ||
416 | max *= 2; | ||
417 | if (unlikely(max >= br->hash_max)) { | ||
418 | printk(KERN_WARNING "%s: Multicast hash table maximum " | ||
419 | "reached, disabling snooping: %s, %d\n", | ||
420 | br->dev->name, port ? port->dev->name : | ||
421 | br->dev->name, | ||
422 | max); | ||
423 | err = -E2BIG; | ||
424 | disable: | ||
425 | br->multicast_disabled = 1; | ||
426 | goto err; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | if (max > mdb->max || elasticity) { | ||
431 | if (mdb->old) { | ||
432 | if (net_ratelimit()) | ||
433 | printk(KERN_INFO "%s: Multicast hash table " | ||
434 | "on fire: %s\n", | ||
435 | br->dev->name, port ? port->dev->name : | ||
436 | br->dev->name); | ||
437 | err = -EEXIST; | ||
438 | goto err; | ||
439 | } | ||
440 | |||
441 | err = br_mdb_rehash(&br->mdb, max, elasticity); | ||
442 | if (err) { | ||
443 | printk(KERN_WARNING "%s: Cannot rehash multicast " | ||
444 | "hash table, disabling snooping: " | ||
445 | "%s, %d, %d\n", | ||
446 | br->dev->name, port ? port->dev->name : | ||
447 | br->dev->name, | ||
448 | mdb->size, err); | ||
449 | goto disable; | ||
450 | } | ||
451 | |||
452 | err = -EAGAIN; | ||
453 | goto err; | ||
454 | } | ||
455 | |||
456 | return NULL; | ||
457 | |||
458 | err: | ||
459 | mp = ERR_PTR(err); | ||
460 | return mp; | ||
461 | } | ||
462 | |||
463 | static struct net_bridge_mdb_entry *br_multicast_new_group( | ||
464 | struct net_bridge *br, struct net_bridge_port *port, __be32 group) | ||
465 | { | ||
466 | struct net_bridge_mdb_htable *mdb = br->mdb; | ||
467 | struct net_bridge_mdb_entry *mp; | ||
468 | int hash; | ||
469 | |||
470 | if (!mdb) { | ||
471 | if (br_mdb_rehash(&br->mdb, BR_HASH_SIZE, 0)) | ||
472 | return NULL; | ||
473 | goto rehash; | ||
474 | } | ||
475 | |||
476 | hash = br_ip_hash(mdb, group); | ||
477 | mp = br_multicast_get_group(br, port, group, hash); | ||
478 | switch (PTR_ERR(mp)) { | ||
479 | case 0: | ||
480 | break; | ||
481 | |||
482 | case -EAGAIN: | ||
483 | rehash: | ||
484 | mdb = br->mdb; | ||
485 | hash = br_ip_hash(mdb, group); | ||
486 | break; | ||
487 | |||
488 | default: | ||
489 | goto out; | ||
490 | } | ||
491 | |||
492 | mp = kzalloc(sizeof(*mp), GFP_ATOMIC); | ||
493 | if (unlikely(!mp)) | ||
494 | goto out; | ||
495 | |||
496 | mp->br = br; | ||
497 | mp->addr = group; | ||
498 | setup_timer(&mp->timer, br_multicast_group_expired, | ||
499 | (unsigned long)mp); | ||
500 | setup_timer(&mp->query_timer, br_multicast_group_query_expired, | ||
501 | (unsigned long)mp); | ||
502 | |||
503 | hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]); | ||
504 | mdb->size++; | ||
505 | |||
506 | out: | ||
507 | return mp; | ||
508 | } | ||
509 | |||
510 | static int br_multicast_add_group(struct net_bridge *br, | ||
511 | struct net_bridge_port *port, __be32 group) | ||
512 | { | ||
513 | struct net_bridge_mdb_entry *mp; | ||
514 | struct net_bridge_port_group *p; | ||
515 | struct net_bridge_port_group **pp; | ||
516 | unsigned long now = jiffies; | ||
517 | int err; | ||
518 | |||
519 | if (ipv4_is_local_multicast(group)) | ||
520 | return 0; | ||
521 | |||
522 | spin_lock(&br->multicast_lock); | ||
523 | if (!netif_running(br->dev) || | ||
524 | (port && port->state == BR_STATE_DISABLED)) | ||
525 | goto out; | ||
526 | |||
527 | mp = br_multicast_new_group(br, port, group); | ||
528 | err = PTR_ERR(mp); | ||
529 | if (unlikely(IS_ERR(mp) || !mp)) | ||
530 | goto err; | ||
531 | |||
532 | if (!port) { | ||
533 | hlist_add_head(&mp->mglist, &br->mglist); | ||
534 | mod_timer(&mp->timer, now + br->multicast_membership_interval); | ||
535 | goto out; | ||
536 | } | ||
537 | |||
538 | for (pp = &mp->ports; (p = *pp); pp = &p->next) { | ||
539 | if (p->port == port) | ||
540 | goto found; | ||
541 | if ((unsigned long)p->port < (unsigned long)port) | ||
542 | break; | ||
543 | } | ||
544 | |||
545 | p = kzalloc(sizeof(*p), GFP_ATOMIC); | ||
546 | err = -ENOMEM; | ||
547 | if (unlikely(!p)) | ||
548 | goto err; | ||
549 | |||
550 | p->addr = group; | ||
551 | p->port = port; | ||
552 | p->next = *pp; | ||
553 | hlist_add_head(&p->mglist, &port->mglist); | ||
554 | setup_timer(&p->timer, br_multicast_port_group_expired, | ||
555 | (unsigned long)p); | ||
556 | setup_timer(&p->query_timer, br_multicast_port_group_query_expired, | ||
557 | (unsigned long)p); | ||
558 | |||
559 | rcu_assign_pointer(*pp, p); | ||
560 | |||
561 | found: | ||
562 | mod_timer(&p->timer, now + br->multicast_membership_interval); | ||
563 | out: | ||
564 | err = 0; | ||
565 | |||
566 | err: | ||
567 | spin_unlock(&br->multicast_lock); | ||
568 | return err; | ||
569 | } | ||
570 | |||
571 | static void br_multicast_router_expired(unsigned long data) | ||
572 | { | ||
573 | struct net_bridge_port *port = (void *)data; | ||
574 | struct net_bridge *br = port->br; | ||
575 | |||
576 | spin_lock(&br->multicast_lock); | ||
577 | if (port->multicast_router != 1 || | ||
578 | timer_pending(&port->multicast_router_timer) || | ||
579 | hlist_unhashed(&port->rlist)) | ||
580 | goto out; | ||
581 | |||
582 | hlist_del_init_rcu(&port->rlist); | ||
583 | |||
584 | out: | ||
585 | spin_unlock(&br->multicast_lock); | ||
586 | } | ||
587 | |||
588 | static void br_multicast_local_router_expired(unsigned long data) | ||
589 | { | ||
590 | } | ||
591 | |||
592 | static void br_multicast_send_query(struct net_bridge *br, | ||
593 | struct net_bridge_port *port, u32 sent) | ||
594 | { | ||
595 | unsigned long time; | ||
596 | struct sk_buff *skb; | ||
597 | |||
598 | if (!netif_running(br->dev) || br->multicast_disabled || | ||
599 | timer_pending(&br->multicast_querier_timer)) | ||
600 | return; | ||
601 | |||
602 | skb = br_multicast_alloc_query(br, 0); | ||
603 | if (!skb) | ||
604 | goto timer; | ||
605 | |||
606 | if (port) { | ||
607 | __skb_push(skb, sizeof(struct ethhdr)); | ||
608 | skb->dev = port->dev; | ||
609 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, | ||
610 | dev_queue_xmit); | ||
611 | } else | ||
612 | netif_rx(skb); | ||
613 | |||
614 | timer: | ||
615 | time = jiffies; | ||
616 | time += sent < br->multicast_startup_query_count ? | ||
617 | br->multicast_startup_query_interval : | ||
618 | br->multicast_query_interval; | ||
619 | mod_timer(port ? &port->multicast_query_timer : | ||
620 | &br->multicast_query_timer, time); | ||
621 | } | ||
622 | |||
623 | static void br_multicast_port_query_expired(unsigned long data) | ||
624 | { | ||
625 | struct net_bridge_port *port = (void *)data; | ||
626 | struct net_bridge *br = port->br; | ||
627 | |||
628 | spin_lock(&br->multicast_lock); | ||
629 | if (port && (port->state == BR_STATE_DISABLED || | ||
630 | port->state == BR_STATE_BLOCKING)) | ||
631 | goto out; | ||
632 | |||
633 | if (port->multicast_startup_queries_sent < | ||
634 | br->multicast_startup_query_count) | ||
635 | port->multicast_startup_queries_sent++; | ||
636 | |||
637 | br_multicast_send_query(port->br, port, | ||
638 | port->multicast_startup_queries_sent); | ||
639 | |||
640 | out: | ||
641 | spin_unlock(&br->multicast_lock); | ||
642 | } | ||
643 | |||
644 | void br_multicast_add_port(struct net_bridge_port *port) | ||
645 | { | ||
646 | port->multicast_router = 1; | ||
647 | |||
648 | setup_timer(&port->multicast_router_timer, br_multicast_router_expired, | ||
649 | (unsigned long)port); | ||
650 | setup_timer(&port->multicast_query_timer, | ||
651 | br_multicast_port_query_expired, (unsigned long)port); | ||
652 | } | ||
653 | |||
654 | void br_multicast_del_port(struct net_bridge_port *port) | ||
655 | { | ||
656 | del_timer_sync(&port->multicast_router_timer); | ||
657 | } | ||
658 | |||
659 | void br_multicast_enable_port(struct net_bridge_port *port) | ||
660 | { | ||
661 | struct net_bridge *br = port->br; | ||
662 | |||
663 | spin_lock(&br->multicast_lock); | ||
664 | if (br->multicast_disabled || !netif_running(br->dev)) | ||
665 | goto out; | ||
666 | |||
667 | port->multicast_startup_queries_sent = 0; | ||
668 | |||
669 | if (try_to_del_timer_sync(&port->multicast_query_timer) >= 0 || | ||
670 | del_timer(&port->multicast_query_timer)) | ||
671 | mod_timer(&port->multicast_query_timer, jiffies); | ||
672 | |||
673 | out: | ||
674 | spin_unlock(&br->multicast_lock); | ||
675 | } | ||
676 | |||
677 | void br_multicast_disable_port(struct net_bridge_port *port) | ||
678 | { | ||
679 | struct net_bridge *br = port->br; | ||
680 | struct net_bridge_port_group *pg; | ||
681 | struct hlist_node *p, *n; | ||
682 | |||
683 | spin_lock(&br->multicast_lock); | ||
684 | hlist_for_each_entry_safe(pg, p, n, &port->mglist, mglist) | ||
685 | br_multicast_del_pg(br, pg); | ||
686 | |||
687 | if (!hlist_unhashed(&port->rlist)) | ||
688 | hlist_del_init_rcu(&port->rlist); | ||
689 | del_timer(&port->multicast_router_timer); | ||
690 | del_timer(&port->multicast_query_timer); | ||
691 | spin_unlock(&br->multicast_lock); | ||
692 | } | ||
693 | |||
694 | static int br_multicast_igmp3_report(struct net_bridge *br, | ||
695 | struct net_bridge_port *port, | ||
696 | struct sk_buff *skb) | ||
697 | { | ||
698 | struct igmpv3_report *ih; | ||
699 | struct igmpv3_grec *grec; | ||
700 | int i; | ||
701 | int len; | ||
702 | int num; | ||
703 | int type; | ||
704 | int err = 0; | ||
705 | __be32 group; | ||
706 | |||
707 | if (!pskb_may_pull(skb, sizeof(*ih))) | ||
708 | return -EINVAL; | ||
709 | |||
710 | ih = igmpv3_report_hdr(skb); | ||
711 | num = ntohs(ih->ngrec); | ||
712 | len = sizeof(*ih); | ||
713 | |||
714 | for (i = 0; i < num; i++) { | ||
715 | len += sizeof(*grec); | ||
716 | if (!pskb_may_pull(skb, len)) | ||
717 | return -EINVAL; | ||
718 | |||
719 | grec = (void *)(skb->data + len); | ||
720 | group = grec->grec_mca; | ||
721 | type = grec->grec_type; | ||
722 | |||
723 | len += grec->grec_nsrcs * 4; | ||
724 | if (!pskb_may_pull(skb, len)) | ||
725 | return -EINVAL; | ||
726 | |||
727 | /* We treat this as an IGMPv2 report for now. */ | ||
728 | switch (type) { | ||
729 | case IGMPV3_MODE_IS_INCLUDE: | ||
730 | case IGMPV3_MODE_IS_EXCLUDE: | ||
731 | case IGMPV3_CHANGE_TO_INCLUDE: | ||
732 | case IGMPV3_CHANGE_TO_EXCLUDE: | ||
733 | case IGMPV3_ALLOW_NEW_SOURCES: | ||
734 | case IGMPV3_BLOCK_OLD_SOURCES: | ||
735 | break; | ||
736 | |||
737 | default: | ||
738 | continue; | ||
739 | } | ||
740 | |||
741 | err = br_multicast_add_group(br, port, group); | ||
742 | if (err) | ||
743 | break; | ||
744 | } | ||
745 | |||
746 | return err; | ||
747 | } | ||
748 | |||
749 | static void br_multicast_mark_router(struct net_bridge *br, | ||
750 | struct net_bridge_port *port) | ||
751 | { | ||
752 | unsigned long now = jiffies; | ||
753 | struct hlist_node *p; | ||
754 | struct hlist_node **h; | ||
755 | |||
756 | if (!port) { | ||
757 | if (br->multicast_router == 1) | ||
758 | mod_timer(&br->multicast_router_timer, | ||
759 | now + br->multicast_querier_interval); | ||
760 | return; | ||
761 | } | ||
762 | |||
763 | if (port->multicast_router != 1) | ||
764 | return; | ||
765 | |||
766 | if (!hlist_unhashed(&port->rlist)) | ||
767 | goto timer; | ||
768 | |||
769 | for (h = &br->router_list.first; | ||
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 | |||
782 | timer: | ||
783 | mod_timer(&port->multicast_router_timer, | ||
784 | now + br->multicast_querier_interval); | ||
785 | } | ||
786 | |||
787 | static void br_multicast_query_received(struct net_bridge *br, | ||
788 | struct net_bridge_port *port, | ||
789 | __be32 saddr) | ||
790 | { | ||
791 | if (saddr) | ||
792 | mod_timer(&br->multicast_querier_timer, | ||
793 | jiffies + br->multicast_querier_interval); | ||
794 | else if (timer_pending(&br->multicast_querier_timer)) | ||
795 | return; | ||
796 | |||
797 | br_multicast_mark_router(br, port); | ||
798 | } | ||
799 | |||
800 | static int br_multicast_query(struct net_bridge *br, | ||
801 | struct net_bridge_port *port, | ||
802 | struct sk_buff *skb) | ||
803 | { | ||
804 | struct iphdr *iph = ip_hdr(skb); | ||
805 | struct igmphdr *ih = igmp_hdr(skb); | ||
806 | struct net_bridge_mdb_entry *mp; | ||
807 | struct igmpv3_query *ih3; | ||
808 | struct net_bridge_port_group *p; | ||
809 | struct net_bridge_port_group **pp; | ||
810 | unsigned long max_delay; | ||
811 | unsigned long now = jiffies; | ||
812 | __be32 group; | ||
813 | |||
814 | spin_lock(&br->multicast_lock); | ||
815 | if (!netif_running(br->dev) || | ||
816 | (port && port->state == BR_STATE_DISABLED)) | ||
817 | goto out; | ||
818 | |||
819 | br_multicast_query_received(br, port, iph->saddr); | ||
820 | |||
821 | group = ih->group; | ||
822 | |||
823 | if (skb->len == sizeof(*ih)) { | ||
824 | max_delay = ih->code * (HZ / IGMP_TIMER_SCALE); | ||
825 | |||
826 | if (!max_delay) { | ||
827 | max_delay = 10 * HZ; | ||
828 | group = 0; | ||
829 | } | ||
830 | } else { | ||
831 | if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) | ||
832 | return -EINVAL; | ||
833 | |||
834 | ih3 = igmpv3_query_hdr(skb); | ||
835 | if (ih3->nsrcs) | ||
836 | return 0; | ||
837 | |||
838 | max_delay = ih3->code ? 1 : | ||
839 | IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE); | ||
840 | } | ||
841 | |||
842 | if (!group) | ||
843 | goto out; | ||
844 | |||
845 | mp = br_mdb_ip_get(br->mdb, group); | ||
846 | if (!mp) | ||
847 | goto out; | ||
848 | |||
849 | max_delay *= br->multicast_last_member_count; | ||
850 | |||
851 | if (!hlist_unhashed(&mp->mglist) && | ||
852 | (timer_pending(&mp->timer) ? | ||
853 | time_after(mp->timer.expires, now + max_delay) : | ||
854 | try_to_del_timer_sync(&mp->timer) >= 0)) | ||
855 | mod_timer(&mp->timer, now + max_delay); | ||
856 | |||
857 | for (pp = &mp->ports; (p = *pp); pp = &p->next) { | ||
858 | if (timer_pending(&p->timer) ? | ||
859 | time_after(p->timer.expires, now + max_delay) : | ||
860 | try_to_del_timer_sync(&p->timer) >= 0) | ||
861 | mod_timer(&mp->timer, now + max_delay); | ||
862 | } | ||
863 | |||
864 | out: | ||
865 | spin_unlock(&br->multicast_lock); | ||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static void br_multicast_leave_group(struct net_bridge *br, | ||
870 | struct net_bridge_port *port, | ||
871 | __be32 group) | ||
872 | { | ||
873 | struct net_bridge_mdb_htable *mdb; | ||
874 | struct net_bridge_mdb_entry *mp; | ||
875 | struct net_bridge_port_group *p; | ||
876 | unsigned long now; | ||
877 | unsigned long time; | ||
878 | |||
879 | if (ipv4_is_local_multicast(group)) | ||
880 | return; | ||
881 | |||
882 | spin_lock(&br->multicast_lock); | ||
883 | if (!netif_running(br->dev) || | ||
884 | (port && port->state == BR_STATE_DISABLED) || | ||
885 | timer_pending(&br->multicast_querier_timer)) | ||
886 | goto out; | ||
887 | |||
888 | mdb = br->mdb; | ||
889 | mp = br_mdb_ip_get(mdb, group); | ||
890 | if (!mp) | ||
891 | goto out; | ||
892 | |||
893 | now = jiffies; | ||
894 | time = now + br->multicast_last_member_count * | ||
895 | br->multicast_last_member_interval; | ||
896 | |||
897 | if (!port) { | ||
898 | if (!hlist_unhashed(&mp->mglist) && | ||
899 | (timer_pending(&mp->timer) ? | ||
900 | time_after(mp->timer.expires, time) : | ||
901 | try_to_del_timer_sync(&mp->timer) >= 0)) { | ||
902 | mod_timer(&mp->timer, time); | ||
903 | |||
904 | mp->queries_sent = 0; | ||
905 | mod_timer(&mp->query_timer, now); | ||
906 | } | ||
907 | |||
908 | goto out; | ||
909 | } | ||
910 | |||
911 | for (p = mp->ports; p; p = p->next) { | ||
912 | if (p->port != port) | ||
913 | continue; | ||
914 | |||
915 | if (!hlist_unhashed(&p->mglist) && | ||
916 | (timer_pending(&p->timer) ? | ||
917 | time_after(p->timer.expires, time) : | ||
918 | try_to_del_timer_sync(&p->timer) >= 0)) { | ||
919 | mod_timer(&p->timer, time); | ||
920 | |||
921 | p->queries_sent = 0; | ||
922 | mod_timer(&p->query_timer, now); | ||
923 | } | ||
924 | |||
925 | break; | ||
926 | } | ||
927 | |||
928 | out: | ||
929 | spin_unlock(&br->multicast_lock); | ||
930 | } | ||
931 | |||
932 | static int br_multicast_ipv4_rcv(struct net_bridge *br, | ||
933 | struct net_bridge_port *port, | ||
934 | struct sk_buff *skb) | ||
935 | { | ||
936 | struct sk_buff *skb2 = skb; | ||
937 | struct iphdr *iph; | ||
938 | struct igmphdr *ih; | ||
939 | unsigned len; | ||
940 | unsigned offset; | ||
941 | int err; | ||
942 | |||
943 | BR_INPUT_SKB_CB(skb)->igmp = 0; | ||
944 | BR_INPUT_SKB_CB(skb)->mrouters_only = 0; | ||
945 | |||
946 | /* We treat OOM as packet loss for now. */ | ||
947 | if (!pskb_may_pull(skb, sizeof(*iph))) | ||
948 | return -EINVAL; | ||
949 | |||
950 | iph = ip_hdr(skb); | ||
951 | |||
952 | if (iph->ihl < 5 || iph->version != 4) | ||
953 | return -EINVAL; | ||
954 | |||
955 | if (!pskb_may_pull(skb, ip_hdrlen(skb))) | ||
956 | return -EINVAL; | ||
957 | |||
958 | iph = ip_hdr(skb); | ||
959 | |||
960 | if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) | ||
961 | return -EINVAL; | ||
962 | |||
963 | if (iph->protocol != IPPROTO_IGMP) | ||
964 | return 0; | ||
965 | |||
966 | len = ntohs(iph->tot_len); | ||
967 | if (skb->len < len || len < ip_hdrlen(skb)) | ||
968 | return -EINVAL; | ||
969 | |||
970 | if (skb->len > len) { | ||
971 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
972 | if (!skb2) | ||
973 | return -ENOMEM; | ||
974 | |||
975 | err = pskb_trim_rcsum(skb2, len); | ||
976 | if (err) | ||
977 | return err; | ||
978 | } | ||
979 | |||
980 | len -= ip_hdrlen(skb2); | ||
981 | offset = skb_network_offset(skb2) + ip_hdrlen(skb2); | ||
982 | __skb_pull(skb2, offset); | ||
983 | skb_reset_transport_header(skb2); | ||
984 | |||
985 | err = -EINVAL; | ||
986 | if (!pskb_may_pull(skb2, sizeof(*ih))) | ||
987 | goto out; | ||
988 | |||
989 | iph = ip_hdr(skb2); | ||
990 | |||
991 | switch (skb2->ip_summed) { | ||
992 | case CHECKSUM_COMPLETE: | ||
993 | if (!csum_fold(skb2->csum)) | ||
994 | break; | ||
995 | /* fall through */ | ||
996 | case CHECKSUM_NONE: | ||
997 | skb2->csum = 0; | ||
998 | if (skb_checksum_complete(skb2)) | ||
999 | return -EINVAL; | ||
1000 | } | ||
1001 | |||
1002 | err = 0; | ||
1003 | |||
1004 | BR_INPUT_SKB_CB(skb)->igmp = 1; | ||
1005 | ih = igmp_hdr(skb2); | ||
1006 | |||
1007 | switch (ih->type) { | ||
1008 | case IGMP_HOST_MEMBERSHIP_REPORT: | ||
1009 | case IGMPV2_HOST_MEMBERSHIP_REPORT: | ||
1010 | BR_INPUT_SKB_CB(skb2)->mrouters_only = 1; | ||
1011 | err = br_multicast_add_group(br, port, ih->group); | ||
1012 | break; | ||
1013 | case IGMPV3_HOST_MEMBERSHIP_REPORT: | ||
1014 | err = br_multicast_igmp3_report(br, port, skb2); | ||
1015 | break; | ||
1016 | case IGMP_HOST_MEMBERSHIP_QUERY: | ||
1017 | err = br_multicast_query(br, port, skb2); | ||
1018 | break; | ||
1019 | case IGMP_HOST_LEAVE_MESSAGE: | ||
1020 | br_multicast_leave_group(br, port, ih->group); | ||
1021 | break; | ||
1022 | } | ||
1023 | |||
1024 | out: | ||
1025 | __skb_push(skb2, offset); | ||
1026 | if (skb2 != skb) | ||
1027 | kfree_skb(skb2); | ||
1028 | return err; | ||
1029 | } | ||
1030 | |||
1031 | int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, | ||
1032 | struct sk_buff *skb) | ||
1033 | { | ||
1034 | if (br->multicast_disabled) | ||
1035 | return 0; | ||
1036 | |||
1037 | switch (skb->protocol) { | ||
1038 | case htons(ETH_P_IP): | ||
1039 | return br_multicast_ipv4_rcv(br, port, skb); | ||
1040 | } | ||
1041 | |||
1042 | return 0; | ||
1043 | } | ||
1044 | |||
1045 | static void br_multicast_query_expired(unsigned long data) | ||
1046 | { | ||
1047 | struct net_bridge *br = (void *)data; | ||
1048 | |||
1049 | spin_lock(&br->multicast_lock); | ||
1050 | if (br->multicast_startup_queries_sent < | ||
1051 | br->multicast_startup_query_count) | ||
1052 | br->multicast_startup_queries_sent++; | ||
1053 | |||
1054 | br_multicast_send_query(br, NULL, br->multicast_startup_queries_sent); | ||
1055 | |||
1056 | spin_unlock(&br->multicast_lock); | ||
1057 | } | ||
1058 | |||
1059 | void br_multicast_init(struct net_bridge *br) | ||
1060 | { | ||
1061 | br->hash_elasticity = 4; | ||
1062 | br->hash_max = 512; | ||
1063 | |||
1064 | br->multicast_router = 1; | ||
1065 | br->multicast_last_member_count = 2; | ||
1066 | br->multicast_startup_query_count = 2; | ||
1067 | |||
1068 | br->multicast_last_member_interval = HZ; | ||
1069 | br->multicast_query_response_interval = 10 * HZ; | ||
1070 | br->multicast_startup_query_interval = 125 * HZ / 4; | ||
1071 | br->multicast_query_interval = 125 * HZ; | ||
1072 | br->multicast_querier_interval = 255 * HZ; | ||
1073 | br->multicast_membership_interval = 260 * HZ; | ||
1074 | |||
1075 | spin_lock_init(&br->multicast_lock); | ||
1076 | setup_timer(&br->multicast_router_timer, | ||
1077 | br_multicast_local_router_expired, 0); | ||
1078 | setup_timer(&br->multicast_querier_timer, | ||
1079 | br_multicast_local_router_expired, 0); | ||
1080 | setup_timer(&br->multicast_query_timer, br_multicast_query_expired, | ||
1081 | (unsigned long)br); | ||
1082 | } | ||
1083 | |||
1084 | void br_multicast_open(struct net_bridge *br) | ||
1085 | { | ||
1086 | br->multicast_startup_queries_sent = 0; | ||
1087 | |||
1088 | if (br->multicast_disabled) | ||
1089 | return; | ||
1090 | |||
1091 | mod_timer(&br->multicast_query_timer, jiffies); | ||
1092 | } | ||
1093 | |||
1094 | void br_multicast_stop(struct net_bridge *br) | ||
1095 | { | ||
1096 | struct net_bridge_mdb_htable *mdb; | ||
1097 | struct net_bridge_mdb_entry *mp; | ||
1098 | struct hlist_node *p, *n; | ||
1099 | u32 ver; | ||
1100 | int i; | ||
1101 | |||
1102 | del_timer_sync(&br->multicast_router_timer); | ||
1103 | del_timer_sync(&br->multicast_querier_timer); | ||
1104 | del_timer_sync(&br->multicast_query_timer); | ||
1105 | |||
1106 | spin_lock_bh(&br->multicast_lock); | ||
1107 | mdb = br->mdb; | ||
1108 | if (!mdb) | ||
1109 | goto out; | ||
1110 | |||
1111 | br->mdb = NULL; | ||
1112 | |||
1113 | ver = mdb->ver; | ||
1114 | for (i = 0; i < mdb->max; i++) { | ||
1115 | hlist_for_each_entry_safe(mp, p, n, &mdb->mhash[i], | ||
1116 | hlist[ver]) { | ||
1117 | del_timer(&mp->timer); | ||
1118 | del_timer(&mp->query_timer); | ||
1119 | call_rcu_bh(&mp->rcu, br_multicast_free_group); | ||
1120 | } | ||
1121 | } | ||
1122 | |||
1123 | if (mdb->old) { | ||
1124 | spin_unlock_bh(&br->multicast_lock); | ||
1125 | synchronize_rcu_bh(); | ||
1126 | spin_lock_bh(&br->multicast_lock); | ||
1127 | WARN_ON(mdb->old); | ||
1128 | } | ||
1129 | |||
1130 | mdb->old = mdb; | ||
1131 | call_rcu_bh(&mdb->rcu, br_mdb_free); | ||
1132 | |||
1133 | out: | ||
1134 | spin_unlock_bh(&br->multicast_lock); | ||
1135 | } | ||
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index fad5a2669d34..44345c9afdd3 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -57,6 +57,41 @@ struct net_bridge_fdb_entry | |||
57 | unsigned char is_static; | 57 | unsigned char is_static; |
58 | }; | 58 | }; |
59 | 59 | ||
60 | struct net_bridge_port_group { | ||
61 | struct net_bridge_port *port; | ||
62 | struct net_bridge_port_group *next; | ||
63 | struct hlist_node mglist; | ||
64 | struct rcu_head rcu; | ||
65 | struct timer_list timer; | ||
66 | struct timer_list query_timer; | ||
67 | __be32 addr; | ||
68 | u32 queries_sent; | ||
69 | }; | ||
70 | |||
71 | struct net_bridge_mdb_entry | ||
72 | { | ||
73 | struct hlist_node hlist[2]; | ||
74 | struct hlist_node mglist; | ||
75 | struct net_bridge *br; | ||
76 | struct net_bridge_port_group *ports; | ||
77 | struct rcu_head rcu; | ||
78 | struct timer_list timer; | ||
79 | struct timer_list query_timer; | ||
80 | __be32 addr; | ||
81 | u32 queries_sent; | ||
82 | }; | ||
83 | |||
84 | struct net_bridge_mdb_htable | ||
85 | { | ||
86 | struct hlist_head *mhash; | ||
87 | struct rcu_head rcu; | ||
88 | struct net_bridge_mdb_htable *old; | ||
89 | u32 size; | ||
90 | u32 max; | ||
91 | u32 secret; | ||
92 | u32 ver; | ||
93 | }; | ||
94 | |||
60 | struct net_bridge_port | 95 | struct net_bridge_port |
61 | { | 96 | { |
62 | struct net_bridge *br; | 97 | struct net_bridge *br; |
@@ -84,6 +119,15 @@ struct net_bridge_port | |||
84 | 119 | ||
85 | unsigned long flags; | 120 | unsigned long flags; |
86 | #define BR_HAIRPIN_MODE 0x00000001 | 121 | #define BR_HAIRPIN_MODE 0x00000001 |
122 | |||
123 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||
124 | u32 multicast_startup_queries_sent; | ||
125 | unsigned char multicast_router; | ||
126 | struct timer_list multicast_router_timer; | ||
127 | struct timer_list multicast_query_timer; | ||
128 | struct hlist_head mglist; | ||
129 | struct hlist_node rlist; | ||
130 | #endif | ||
87 | }; | 131 | }; |
88 | 132 | ||
89 | struct net_bridge | 133 | struct net_bridge |
@@ -124,6 +168,35 @@ struct net_bridge | |||
124 | unsigned char topology_change; | 168 | unsigned char topology_change; |
125 | unsigned char topology_change_detected; | 169 | unsigned char topology_change_detected; |
126 | 170 | ||
171 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||
172 | unsigned char multicast_router; | ||
173 | |||
174 | u8 multicast_disabled:1; | ||
175 | |||
176 | u32 hash_elasticity; | ||
177 | u32 hash_max; | ||
178 | |||
179 | u32 multicast_last_member_count; | ||
180 | u32 multicast_startup_queries_sent; | ||
181 | u32 multicast_startup_query_count; | ||
182 | |||
183 | unsigned long multicast_last_member_interval; | ||
184 | unsigned long multicast_membership_interval; | ||
185 | unsigned long multicast_querier_interval; | ||
186 | unsigned long multicast_query_interval; | ||
187 | unsigned long multicast_query_response_interval; | ||
188 | unsigned long multicast_startup_query_interval; | ||
189 | |||
190 | spinlock_t multicast_lock; | ||
191 | struct net_bridge_mdb_htable *mdb; | ||
192 | struct hlist_head router_list; | ||
193 | struct hlist_head mglist; | ||
194 | |||
195 | struct timer_list multicast_router_timer; | ||
196 | struct timer_list multicast_querier_timer; | ||
197 | struct timer_list multicast_query_timer; | ||
198 | #endif | ||
199 | |||
127 | struct timer_list hello_timer; | 200 | struct timer_list hello_timer; |
128 | struct timer_list tcn_timer; | 201 | struct timer_list tcn_timer; |
129 | struct timer_list topology_change_timer; | 202 | struct timer_list topology_change_timer; |
@@ -133,6 +206,8 @@ struct net_bridge | |||
133 | 206 | ||
134 | struct br_input_skb_cb { | 207 | struct br_input_skb_cb { |
135 | struct net_device *brdev; | 208 | struct net_device *brdev; |
209 | int igmp; | ||
210 | int mrouters_only; | ||
136 | }; | 211 | }; |
137 | 212 | ||
138 | #define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb) | 213 | #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 | extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); | 279 | extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); |
205 | extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg); | 280 | extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg); |
206 | 281 | ||
282 | /* br_multicast.c */ | ||
283 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||
284 | extern int br_multicast_rcv(struct net_bridge *br, | ||
285 | struct net_bridge_port *port, | ||
286 | struct sk_buff *skb); | ||
287 | extern struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, | ||
288 | struct sk_buff *skb); | ||
289 | extern void br_multicast_add_port(struct net_bridge_port *port); | ||
290 | extern void br_multicast_del_port(struct net_bridge_port *port); | ||
291 | extern void br_multicast_enable_port(struct net_bridge_port *port); | ||
292 | extern void br_multicast_disable_port(struct net_bridge_port *port); | ||
293 | extern void br_multicast_init(struct net_bridge *br); | ||
294 | extern void br_multicast_open(struct net_bridge *br); | ||
295 | extern void br_multicast_stop(struct net_bridge *br); | ||
296 | #else | ||
297 | static inline int br_multicast_rcv(struct net_bridge *br, | ||
298 | struct net_bridge_port *port, | ||
299 | struct sk_buff *skb) | ||
300 | { | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static inline struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, | ||
305 | struct sk_buff *skb) | ||
306 | { | ||
307 | return NULL; | ||
308 | } | ||
309 | |||
310 | static inline void br_multicast_add_port(struct net_bridge_port *port) | ||
311 | { | ||
312 | } | ||
313 | |||
314 | static inline void br_multicast_del_port(struct net_bridge_port *port) | ||
315 | { | ||
316 | } | ||
317 | |||
318 | static inline void br_multicast_enable_port(struct net_bridge_port *port) | ||
319 | { | ||
320 | } | ||
321 | |||
322 | static inline void br_multicast_disable_port(struct net_bridge_port *port) | ||
323 | { | ||
324 | } | ||
325 | |||
326 | static inline void br_multicast_init(struct net_bridge *br) | ||
327 | { | ||
328 | } | ||
329 | |||
330 | static inline void br_multicast_open(struct net_bridge *br) | ||
331 | { | ||
332 | } | ||
333 | |||
334 | static inline void br_multicast_stop(struct net_bridge *br) | ||
335 | { | ||
336 | } | ||
337 | #endif | ||
338 | |||
339 | static inline bool br_multicast_is_router(struct net_bridge *br) | ||
340 | { | ||
341 | return br->multicast_router == 2 || | ||
342 | (br->multicast_router == 1 && | ||
343 | timer_pending(&br->multicast_router_timer)); | ||
344 | } | ||
345 | |||
207 | /* br_netfilter.c */ | 346 | /* br_netfilter.c */ |
208 | #ifdef CONFIG_BRIDGE_NETFILTER | 347 | #ifdef CONFIG_BRIDGE_NETFILTER |
209 | extern int br_netfilter_init(void); | 348 | extern int br_netfilter_init(void); |