Commit | Line | Data |
---|---|---|
5b163288 NA |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | // Copyright (c) 2020, Nikolay Aleksandrov <nikolay@nvidia.com> | |
3 | #include <linux/err.h> | |
4 | #include <linux/export.h> | |
5 | #include <linux/if_ether.h> | |
6 | #include <linux/igmp.h> | |
7 | #include <linux/in.h> | |
8 | #include <linux/jhash.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/log2.h> | |
11 | #include <linux/netdevice.h> | |
12 | #include <linux/netfilter_bridge.h> | |
13 | #include <linux/random.h> | |
14 | #include <linux/rculist.h> | |
15 | #include <linux/skbuff.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/timer.h> | |
18 | #include <linux/inetdevice.h> | |
19 | #include <linux/mroute.h> | |
20 | #include <net/ip.h> | |
21 | #include <net/switchdev.h> | |
22 | #if IS_ENABLED(CONFIG_IPV6) | |
23 | #include <linux/icmpv6.h> | |
24 | #include <net/ipv6.h> | |
25 | #include <net/mld.h> | |
26 | #include <net/ip6_checksum.h> | |
27 | #include <net/addrconf.h> | |
28 | #endif | |
29 | ||
30 | #include "br_private.h" | |
31 | #include "br_private_mcast_eht.h" | |
32 | ||
baa74d39 NA |
33 | static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg, |
34 | union net_bridge_eht_addr *src_addr, | |
35 | union net_bridge_eht_addr *h_addr); | |
adc47037 NA |
36 | static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx, |
37 | struct net_bridge_port_group *pg, | |
baa74d39 NA |
38 | union net_bridge_eht_addr *src_addr, |
39 | union net_bridge_eht_addr *h_addr, | |
40 | int filter_mode, | |
41 | bool allow_zero_src); | |
42 | ||
5b163288 NA |
43 | static struct net_bridge_group_eht_host * |
44 | br_multicast_eht_host_lookup(struct net_bridge_port_group *pg, | |
45 | union net_bridge_eht_addr *h_addr) | |
46 | { | |
47 | struct rb_node *node = pg->eht_host_tree.rb_node; | |
48 | ||
49 | while (node) { | |
50 | struct net_bridge_group_eht_host *this; | |
51 | int result; | |
52 | ||
53 | this = rb_entry(node, struct net_bridge_group_eht_host, | |
54 | rb_node); | |
55 | result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr)); | |
56 | if (result < 0) | |
57 | node = node->rb_left; | |
58 | else if (result > 0) | |
59 | node = node->rb_right; | |
60 | else | |
61 | return this; | |
62 | } | |
63 | ||
64 | return NULL; | |
65 | } | |
66 | ||
67 | static int br_multicast_eht_host_filter_mode(struct net_bridge_port_group *pg, | |
68 | union net_bridge_eht_addr *h_addr) | |
69 | { | |
70 | struct net_bridge_group_eht_host *eht_host; | |
71 | ||
72 | eht_host = br_multicast_eht_host_lookup(pg, h_addr); | |
73 | if (!eht_host) | |
74 | return MCAST_INCLUDE; | |
75 | ||
76 | return eht_host->filter_mode; | |
77 | } | |
78 | ||
baa74d39 NA |
79 | static struct net_bridge_group_eht_set_entry * |
80 | br_multicast_eht_set_entry_lookup(struct net_bridge_group_eht_set *eht_set, | |
81 | union net_bridge_eht_addr *h_addr) | |
82 | { | |
83 | struct rb_node *node = eht_set->entry_tree.rb_node; | |
84 | ||
85 | while (node) { | |
86 | struct net_bridge_group_eht_set_entry *this; | |
87 | int result; | |
88 | ||
3e841bac NA |
89 | this = rb_entry(node, struct net_bridge_group_eht_set_entry, |
90 | rb_node); | |
91 | result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr)); | |
92 | if (result < 0) | |
93 | node = node->rb_left; | |
94 | else if (result > 0) | |
95 | node = node->rb_right; | |
96 | else | |
97 | return this; | |
baa74d39 NA |
98 | } |
99 | ||
100 | return NULL; | |
101 | } | |
102 | ||
103 | static struct net_bridge_group_eht_set * | |
104 | br_multicast_eht_set_lookup(struct net_bridge_port_group *pg, | |
105 | union net_bridge_eht_addr *src_addr) | |
106 | { | |
107 | struct rb_node *node = pg->eht_set_tree.rb_node; | |
108 | ||
109 | while (node) { | |
110 | struct net_bridge_group_eht_set *this; | |
111 | int result; | |
112 | ||
113 | this = rb_entry(node, struct net_bridge_group_eht_set, | |
114 | rb_node); | |
115 | result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr)); | |
116 | if (result < 0) | |
117 | node = node->rb_left; | |
118 | else if (result > 0) | |
119 | node = node->rb_right; | |
120 | else | |
121 | return this; | |
122 | } | |
123 | ||
124 | return NULL; | |
125 | } | |
126 | ||
5b163288 NA |
127 | static void __eht_destroy_host(struct net_bridge_group_eht_host *eht_host) |
128 | { | |
129 | WARN_ON(!hlist_empty(&eht_host->set_entries)); | |
130 | ||
89268b05 NA |
131 | br_multicast_eht_hosts_dec(eht_host->pg); |
132 | ||
5b163288 NA |
133 | rb_erase(&eht_host->rb_node, &eht_host->pg->eht_host_tree); |
134 | RB_CLEAR_NODE(&eht_host->rb_node); | |
135 | kfree(eht_host); | |
136 | } | |
137 | ||
baa74d39 NA |
138 | static void br_multicast_destroy_eht_set_entry(struct net_bridge_mcast_gc *gc) |
139 | { | |
140 | struct net_bridge_group_eht_set_entry *set_h; | |
141 | ||
142 | set_h = container_of(gc, struct net_bridge_group_eht_set_entry, mcast_gc); | |
143 | WARN_ON(!RB_EMPTY_NODE(&set_h->rb_node)); | |
144 | ||
292a089d | 145 | timer_shutdown_sync(&set_h->timer); |
baa74d39 NA |
146 | kfree(set_h); |
147 | } | |
148 | ||
149 | static void br_multicast_destroy_eht_set(struct net_bridge_mcast_gc *gc) | |
150 | { | |
151 | struct net_bridge_group_eht_set *eht_set; | |
152 | ||
153 | eht_set = container_of(gc, struct net_bridge_group_eht_set, mcast_gc); | |
154 | WARN_ON(!RB_EMPTY_NODE(&eht_set->rb_node)); | |
155 | WARN_ON(!RB_EMPTY_ROOT(&eht_set->entry_tree)); | |
156 | ||
292a089d | 157 | timer_shutdown_sync(&eht_set->timer); |
baa74d39 NA |
158 | kfree(eht_set); |
159 | } | |
160 | ||
161 | static void __eht_del_set_entry(struct net_bridge_group_eht_set_entry *set_h) | |
162 | { | |
163 | struct net_bridge_group_eht_host *eht_host = set_h->h_parent; | |
164 | union net_bridge_eht_addr zero_addr; | |
165 | ||
166 | rb_erase(&set_h->rb_node, &set_h->eht_set->entry_tree); | |
167 | RB_CLEAR_NODE(&set_h->rb_node); | |
168 | hlist_del_init(&set_h->host_list); | |
169 | memset(&zero_addr, 0, sizeof(zero_addr)); | |
170 | if (memcmp(&set_h->h_addr, &zero_addr, sizeof(zero_addr))) | |
171 | eht_host->num_entries--; | |
172 | hlist_add_head(&set_h->mcast_gc.gc_node, &set_h->br->mcast_gc_list); | |
173 | queue_work(system_long_wq, &set_h->br->mcast_gc_work); | |
174 | ||
175 | if (hlist_empty(&eht_host->set_entries)) | |
176 | __eht_destroy_host(eht_host); | |
177 | } | |
178 | ||
179 | static void br_multicast_del_eht_set(struct net_bridge_group_eht_set *eht_set) | |
180 | { | |
181 | struct net_bridge_group_eht_set_entry *set_h; | |
182 | struct rb_node *node; | |
183 | ||
184 | while ((node = rb_first(&eht_set->entry_tree))) { | |
185 | set_h = rb_entry(node, struct net_bridge_group_eht_set_entry, | |
186 | rb_node); | |
187 | __eht_del_set_entry(set_h); | |
188 | } | |
189 | ||
190 | rb_erase(&eht_set->rb_node, &eht_set->pg->eht_set_tree); | |
191 | RB_CLEAR_NODE(&eht_set->rb_node); | |
192 | hlist_add_head(&eht_set->mcast_gc.gc_node, &eht_set->br->mcast_gc_list); | |
193 | queue_work(system_long_wq, &eht_set->br->mcast_gc_work); | |
194 | } | |
195 | ||
196 | void br_multicast_eht_clean_sets(struct net_bridge_port_group *pg) | |
197 | { | |
198 | struct net_bridge_group_eht_set *eht_set; | |
199 | struct rb_node *node; | |
200 | ||
201 | while ((node = rb_first(&pg->eht_set_tree))) { | |
202 | eht_set = rb_entry(node, struct net_bridge_group_eht_set, | |
203 | rb_node); | |
204 | br_multicast_del_eht_set(eht_set); | |
205 | } | |
206 | } | |
207 | ||
208 | static void br_multicast_eht_set_entry_expired(struct timer_list *t) | |
209 | { | |
210 | struct net_bridge_group_eht_set_entry *set_h = from_timer(set_h, t, timer); | |
211 | struct net_bridge *br = set_h->br; | |
212 | ||
213 | spin_lock(&br->multicast_lock); | |
214 | if (RB_EMPTY_NODE(&set_h->rb_node) || timer_pending(&set_h->timer)) | |
215 | goto out; | |
216 | ||
217 | br_multicast_del_eht_set_entry(set_h->eht_set->pg, | |
218 | &set_h->eht_set->src_addr, | |
219 | &set_h->h_addr); | |
220 | out: | |
221 | spin_unlock(&br->multicast_lock); | |
222 | } | |
223 | ||
224 | static void br_multicast_eht_set_expired(struct timer_list *t) | |
225 | { | |
226 | struct net_bridge_group_eht_set *eht_set = from_timer(eht_set, t, | |
227 | timer); | |
228 | struct net_bridge *br = eht_set->br; | |
229 | ||
230 | spin_lock(&br->multicast_lock); | |
231 | if (RB_EMPTY_NODE(&eht_set->rb_node) || timer_pending(&eht_set->timer)) | |
232 | goto out; | |
233 | ||
234 | br_multicast_del_eht_set(eht_set); | |
235 | out: | |
236 | spin_unlock(&br->multicast_lock); | |
237 | } | |
238 | ||
5b163288 NA |
239 | static struct net_bridge_group_eht_host * |
240 | __eht_lookup_create_host(struct net_bridge_port_group *pg, | |
241 | union net_bridge_eht_addr *h_addr, | |
242 | unsigned char filter_mode) | |
243 | { | |
244 | struct rb_node **link = &pg->eht_host_tree.rb_node, *parent = NULL; | |
245 | struct net_bridge_group_eht_host *eht_host; | |
246 | ||
247 | while (*link) { | |
248 | struct net_bridge_group_eht_host *this; | |
249 | int result; | |
250 | ||
251 | this = rb_entry(*link, struct net_bridge_group_eht_host, | |
252 | rb_node); | |
253 | result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr)); | |
254 | parent = *link; | |
255 | if (result < 0) | |
256 | link = &((*link)->rb_left); | |
257 | else if (result > 0) | |
258 | link = &((*link)->rb_right); | |
259 | else | |
260 | return this; | |
261 | } | |
262 | ||
89268b05 NA |
263 | if (br_multicast_eht_hosts_over_limit(pg)) |
264 | return NULL; | |
265 | ||
5b163288 NA |
266 | eht_host = kzalloc(sizeof(*eht_host), GFP_ATOMIC); |
267 | if (!eht_host) | |
268 | return NULL; | |
269 | ||
270 | memcpy(&eht_host->h_addr, h_addr, sizeof(*h_addr)); | |
271 | INIT_HLIST_HEAD(&eht_host->set_entries); | |
272 | eht_host->pg = pg; | |
273 | eht_host->filter_mode = filter_mode; | |
274 | ||
275 | rb_link_node(&eht_host->rb_node, parent, link); | |
276 | rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree); | |
277 | ||
89268b05 NA |
278 | br_multicast_eht_hosts_inc(pg); |
279 | ||
5b163288 NA |
280 | return eht_host; |
281 | } | |
baa74d39 NA |
282 | |
283 | static struct net_bridge_group_eht_set_entry * | |
284 | __eht_lookup_create_set_entry(struct net_bridge *br, | |
285 | struct net_bridge_group_eht_set *eht_set, | |
286 | struct net_bridge_group_eht_host *eht_host, | |
287 | bool allow_zero_src) | |
288 | { | |
289 | struct rb_node **link = &eht_set->entry_tree.rb_node, *parent = NULL; | |
290 | struct net_bridge_group_eht_set_entry *set_h; | |
291 | ||
292 | while (*link) { | |
293 | struct net_bridge_group_eht_set_entry *this; | |
294 | int result; | |
295 | ||
296 | this = rb_entry(*link, struct net_bridge_group_eht_set_entry, | |
297 | rb_node); | |
298 | result = memcmp(&eht_host->h_addr, &this->h_addr, | |
299 | sizeof(union net_bridge_eht_addr)); | |
300 | parent = *link; | |
301 | if (result < 0) | |
302 | link = &((*link)->rb_left); | |
303 | else if (result > 0) | |
304 | link = &((*link)->rb_right); | |
305 | else | |
306 | return this; | |
307 | } | |
308 | ||
309 | /* always allow auto-created zero entry */ | |
310 | if (!allow_zero_src && eht_host->num_entries >= PG_SRC_ENT_LIMIT) | |
311 | return NULL; | |
312 | ||
313 | set_h = kzalloc(sizeof(*set_h), GFP_ATOMIC); | |
314 | if (!set_h) | |
315 | return NULL; | |
316 | ||
317 | memcpy(&set_h->h_addr, &eht_host->h_addr, | |
318 | sizeof(union net_bridge_eht_addr)); | |
319 | set_h->mcast_gc.destroy = br_multicast_destroy_eht_set_entry; | |
320 | set_h->eht_set = eht_set; | |
321 | set_h->h_parent = eht_host; | |
322 | set_h->br = br; | |
323 | timer_setup(&set_h->timer, br_multicast_eht_set_entry_expired, 0); | |
324 | ||
325 | hlist_add_head(&set_h->host_list, &eht_host->set_entries); | |
326 | rb_link_node(&set_h->rb_node, parent, link); | |
327 | rb_insert_color(&set_h->rb_node, &eht_set->entry_tree); | |
328 | /* we must not count the auto-created zero entry otherwise we won't be | |
329 | * able to track the full list of PG_SRC_ENT_LIMIT entries | |
330 | */ | |
331 | if (!allow_zero_src) | |
332 | eht_host->num_entries++; | |
333 | ||
334 | return set_h; | |
335 | } | |
336 | ||
337 | static struct net_bridge_group_eht_set * | |
338 | __eht_lookup_create_set(struct net_bridge_port_group *pg, | |
339 | union net_bridge_eht_addr *src_addr) | |
340 | { | |
341 | struct rb_node **link = &pg->eht_set_tree.rb_node, *parent = NULL; | |
342 | struct net_bridge_group_eht_set *eht_set; | |
343 | ||
344 | while (*link) { | |
345 | struct net_bridge_group_eht_set *this; | |
346 | int result; | |
347 | ||
348 | this = rb_entry(*link, struct net_bridge_group_eht_set, | |
349 | rb_node); | |
350 | result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr)); | |
351 | parent = *link; | |
352 | if (result < 0) | |
353 | link = &((*link)->rb_left); | |
354 | else if (result > 0) | |
355 | link = &((*link)->rb_right); | |
356 | else | |
357 | return this; | |
358 | } | |
359 | ||
360 | eht_set = kzalloc(sizeof(*eht_set), GFP_ATOMIC); | |
361 | if (!eht_set) | |
362 | return NULL; | |
363 | ||
364 | memcpy(&eht_set->src_addr, src_addr, sizeof(*src_addr)); | |
365 | eht_set->mcast_gc.destroy = br_multicast_destroy_eht_set; | |
366 | eht_set->pg = pg; | |
367 | eht_set->br = pg->key.port->br; | |
368 | eht_set->entry_tree = RB_ROOT; | |
369 | timer_setup(&eht_set->timer, br_multicast_eht_set_expired, 0); | |
370 | ||
371 | rb_link_node(&eht_set->rb_node, parent, link); | |
372 | rb_insert_color(&eht_set->rb_node, &pg->eht_set_tree); | |
373 | ||
374 | return eht_set; | |
375 | } | |
376 | ||
474ddb37 NA |
377 | static void br_multicast_ip_src_to_eht_addr(const struct br_ip *src, |
378 | union net_bridge_eht_addr *dest) | |
379 | { | |
380 | switch (src->proto) { | |
381 | case htons(ETH_P_IP): | |
382 | dest->ip4 = src->src.ip4; | |
383 | break; | |
384 | #if IS_ENABLED(CONFIG_IPV6) | |
385 | case htons(ETH_P_IPV6): | |
386 | memcpy(&dest->ip6, &src->src.ip6, sizeof(struct in6_addr)); | |
387 | break; | |
388 | #endif | |
389 | } | |
390 | } | |
391 | ||
adc47037 NA |
392 | static void br_eht_convert_host_filter_mode(const struct net_bridge_mcast *brmctx, |
393 | struct net_bridge_port_group *pg, | |
c9739016 NA |
394 | union net_bridge_eht_addr *h_addr, |
395 | int filter_mode) | |
396 | { | |
397 | struct net_bridge_group_eht_host *eht_host; | |
398 | union net_bridge_eht_addr zero_addr; | |
399 | ||
400 | eht_host = br_multicast_eht_host_lookup(pg, h_addr); | |
401 | if (eht_host) | |
402 | eht_host->filter_mode = filter_mode; | |
403 | ||
404 | memset(&zero_addr, 0, sizeof(zero_addr)); | |
405 | switch (filter_mode) { | |
406 | case MCAST_INCLUDE: | |
407 | br_multicast_del_eht_set_entry(pg, &zero_addr, h_addr); | |
408 | break; | |
409 | case MCAST_EXCLUDE: | |
adc47037 NA |
410 | br_multicast_create_eht_set_entry(brmctx, pg, &zero_addr, |
411 | h_addr, MCAST_EXCLUDE, | |
c9739016 NA |
412 | true); |
413 | break; | |
414 | } | |
415 | } | |
416 | ||
adc47037 NA |
417 | static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx, |
418 | struct net_bridge_port_group *pg, | |
baa74d39 NA |
419 | union net_bridge_eht_addr *src_addr, |
420 | union net_bridge_eht_addr *h_addr, | |
421 | int filter_mode, | |
422 | bool allow_zero_src) | |
423 | { | |
424 | struct net_bridge_group_eht_set_entry *set_h; | |
425 | struct net_bridge_group_eht_host *eht_host; | |
426 | struct net_bridge *br = pg->key.port->br; | |
427 | struct net_bridge_group_eht_set *eht_set; | |
428 | union net_bridge_eht_addr zero_addr; | |
429 | ||
430 | memset(&zero_addr, 0, sizeof(zero_addr)); | |
431 | if (!allow_zero_src && !memcmp(src_addr, &zero_addr, sizeof(zero_addr))) | |
432 | return; | |
433 | ||
434 | eht_set = __eht_lookup_create_set(pg, src_addr); | |
435 | if (!eht_set) | |
436 | return; | |
437 | ||
438 | eht_host = __eht_lookup_create_host(pg, h_addr, filter_mode); | |
439 | if (!eht_host) | |
440 | goto fail_host; | |
441 | ||
442 | set_h = __eht_lookup_create_set_entry(br, eht_set, eht_host, | |
443 | allow_zero_src); | |
444 | if (!set_h) | |
445 | goto fail_set_entry; | |
446 | ||
adc47037 NA |
447 | mod_timer(&set_h->timer, jiffies + br_multicast_gmi(brmctx)); |
448 | mod_timer(&eht_set->timer, jiffies + br_multicast_gmi(brmctx)); | |
baa74d39 NA |
449 | |
450 | return; | |
451 | ||
452 | fail_set_entry: | |
453 | if (hlist_empty(&eht_host->set_entries)) | |
454 | __eht_destroy_host(eht_host); | |
455 | fail_host: | |
456 | if (RB_EMPTY_ROOT(&eht_set->entry_tree)) | |
457 | br_multicast_del_eht_set(eht_set); | |
458 | } | |
459 | ||
460 | static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg, | |
461 | union net_bridge_eht_addr *src_addr, | |
462 | union net_bridge_eht_addr *h_addr) | |
463 | { | |
464 | struct net_bridge_group_eht_set_entry *set_h; | |
465 | struct net_bridge_group_eht_set *eht_set; | |
466 | bool set_deleted = false; | |
467 | ||
468 | eht_set = br_multicast_eht_set_lookup(pg, src_addr); | |
469 | if (!eht_set) | |
470 | goto out; | |
471 | ||
472 | set_h = br_multicast_eht_set_entry_lookup(eht_set, h_addr); | |
473 | if (!set_h) | |
474 | goto out; | |
475 | ||
476 | __eht_del_set_entry(set_h); | |
477 | ||
478 | if (RB_EMPTY_ROOT(&eht_set->entry_tree)) { | |
479 | br_multicast_del_eht_set(eht_set); | |
480 | set_deleted = true; | |
481 | } | |
482 | ||
483 | out: | |
484 | return set_deleted; | |
485 | } | |
dba6b0a5 NA |
486 | |
487 | static void br_multicast_del_eht_host(struct net_bridge_port_group *pg, | |
488 | union net_bridge_eht_addr *h_addr) | |
489 | { | |
490 | struct net_bridge_group_eht_set_entry *set_h; | |
491 | struct net_bridge_group_eht_host *eht_host; | |
492 | struct hlist_node *tmp; | |
493 | ||
494 | eht_host = br_multicast_eht_host_lookup(pg, h_addr); | |
495 | if (!eht_host) | |
496 | return; | |
497 | ||
498 | hlist_for_each_entry_safe(set_h, tmp, &eht_host->set_entries, host_list) | |
499 | br_multicast_del_eht_set_entry(set_h->eht_set->pg, | |
500 | &set_h->eht_set->src_addr, | |
501 | &set_h->h_addr); | |
502 | } | |
474ddb37 | 503 | |
e09cf582 | 504 | /* create new set entries from reports */ |
adc47037 NA |
505 | static void __eht_create_set_entries(const struct net_bridge_mcast *brmctx, |
506 | struct net_bridge_port_group *pg, | |
e09cf582 NA |
507 | union net_bridge_eht_addr *h_addr, |
508 | void *srcs, | |
509 | u32 nsrcs, | |
510 | size_t addr_size, | |
511 | int filter_mode) | |
474ddb37 NA |
512 | { |
513 | union net_bridge_eht_addr eht_src_addr; | |
514 | u32 src_idx; | |
515 | ||
516 | memset(&eht_src_addr, 0, sizeof(eht_src_addr)); | |
517 | for (src_idx = 0; src_idx < nsrcs; src_idx++) { | |
518 | memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size); | |
adc47037 NA |
519 | br_multicast_create_eht_set_entry(brmctx, pg, &eht_src_addr, |
520 | h_addr, filter_mode, | |
474ddb37 NA |
521 | false); |
522 | } | |
523 | } | |
524 | ||
e09cf582 NA |
525 | /* delete existing set entries and their (S,G) entries if they were the last */ |
526 | static bool __eht_del_set_entries(struct net_bridge_port_group *pg, | |
527 | union net_bridge_eht_addr *h_addr, | |
528 | void *srcs, | |
529 | u32 nsrcs, | |
530 | size_t addr_size) | |
474ddb37 | 531 | { |
474ddb37 NA |
532 | union net_bridge_eht_addr eht_src_addr; |
533 | struct net_bridge_group_src *src_ent; | |
6aa2c371 | 534 | bool changed = false; |
474ddb37 NA |
535 | struct br_ip src_ip; |
536 | u32 src_idx; | |
537 | ||
474ddb37 | 538 | memset(&eht_src_addr, 0, sizeof(eht_src_addr)); |
e09cf582 NA |
539 | memset(&src_ip, 0, sizeof(src_ip)); |
540 | src_ip.proto = pg->key.addr.proto; | |
474ddb37 NA |
541 | for (src_idx = 0; src_idx < nsrcs; src_idx++) { |
542 | memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size); | |
e09cf582 | 543 | if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr, h_addr)) |
6aa2c371 NA |
544 | continue; |
545 | memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size); | |
546 | src_ent = br_multicast_find_group_src(pg, &src_ip); | |
547 | if (!src_ent) | |
548 | continue; | |
549 | br_multicast_del_group_src(src_ent, true); | |
550 | changed = true; | |
474ddb37 NA |
551 | } |
552 | ||
553 | return changed; | |
554 | } | |
555 | ||
adc47037 NA |
556 | static bool br_multicast_eht_allow(const struct net_bridge_mcast *brmctx, |
557 | struct net_bridge_port_group *pg, | |
474ddb37 NA |
558 | union net_bridge_eht_addr *h_addr, |
559 | void *srcs, | |
560 | u32 nsrcs, | |
561 | size_t addr_size) | |
562 | { | |
563 | bool changed = false; | |
564 | ||
565 | switch (br_multicast_eht_host_filter_mode(pg, h_addr)) { | |
566 | case MCAST_INCLUDE: | |
adc47037 NA |
567 | __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, |
568 | addr_size, MCAST_INCLUDE); | |
474ddb37 NA |
569 | break; |
570 | case MCAST_EXCLUDE: | |
e09cf582 NA |
571 | changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs, |
572 | addr_size); | |
474ddb37 NA |
573 | break; |
574 | } | |
575 | ||
576 | return changed; | |
577 | } | |
578 | ||
adc47037 NA |
579 | static bool br_multicast_eht_block(const struct net_bridge_mcast *brmctx, |
580 | struct net_bridge_port_group *pg, | |
474ddb37 NA |
581 | union net_bridge_eht_addr *h_addr, |
582 | void *srcs, | |
583 | u32 nsrcs, | |
584 | size_t addr_size) | |
585 | { | |
586 | bool changed = false; | |
587 | ||
588 | switch (br_multicast_eht_host_filter_mode(pg, h_addr)) { | |
589 | case MCAST_INCLUDE: | |
e09cf582 NA |
590 | changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs, |
591 | addr_size); | |
474ddb37 NA |
592 | break; |
593 | case MCAST_EXCLUDE: | |
adc47037 | 594 | __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
e09cf582 | 595 | MCAST_EXCLUDE); |
474ddb37 NA |
596 | break; |
597 | } | |
598 | ||
599 | return changed; | |
600 | } | |
601 | ||
ddc255d9 | 602 | /* flush_entries is true when changing mode */ |
adc47037 NA |
603 | static bool __eht_inc_exc(const struct net_bridge_mcast *brmctx, |
604 | struct net_bridge_port_group *pg, | |
ddc255d9 NA |
605 | union net_bridge_eht_addr *h_addr, |
606 | void *srcs, | |
607 | u32 nsrcs, | |
608 | size_t addr_size, | |
609 | unsigned char filter_mode, | |
610 | bool to_report) | |
611 | { | |
612 | bool changed = false, flush_entries = to_report; | |
613 | union net_bridge_eht_addr eht_src_addr; | |
ddc255d9 NA |
614 | |
615 | if (br_multicast_eht_host_filter_mode(pg, h_addr) != filter_mode) | |
616 | flush_entries = true; | |
617 | ||
618 | memset(&eht_src_addr, 0, sizeof(eht_src_addr)); | |
619 | /* if we're changing mode del host and its entries */ | |
620 | if (flush_entries) | |
621 | br_multicast_del_eht_host(pg, h_addr); | |
adc47037 | 622 | __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
e09cf582 | 623 | filter_mode); |
ddc255d9 NA |
624 | /* we can be missing sets only if we've deleted some entries */ |
625 | if (flush_entries) { | |
b66bf55b | 626 | struct net_bridge_group_eht_set *eht_set; |
ddc255d9 NA |
627 | struct net_bridge_group_src *src_ent; |
628 | struct hlist_node *tmp; | |
629 | ||
630 | hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) { | |
631 | br_multicast_ip_src_to_eht_addr(&src_ent->addr, | |
632 | &eht_src_addr); | |
633 | if (!br_multicast_eht_set_lookup(pg, &eht_src_addr)) { | |
d5a10222 | 634 | br_multicast_del_group_src(src_ent, true); |
ddc255d9 NA |
635 | changed = true; |
636 | continue; | |
637 | } | |
b66bf55b NA |
638 | /* this is an optimization for TO_INCLUDE where we lower |
639 | * the set's timeout to LMQT to catch timeout hosts: | |
640 | * - host A (timing out): set entries X, Y | |
641 | * - host B: set entry Z (new from current TO_INCLUDE) | |
642 | * sends BLOCK Z after LMQT but host A's EHT | |
643 | * entries still exist (unless lowered to LMQT | |
644 | * so they can timeout with the S,Gs) | |
645 | * => we wait another LMQT, when we can just delete the | |
646 | * group immediately | |
647 | */ | |
648 | if (!(src_ent->flags & BR_SGRP_F_SEND) || | |
649 | filter_mode != MCAST_INCLUDE || | |
650 | !to_report) | |
651 | continue; | |
652 | eht_set = br_multicast_eht_set_lookup(pg, | |
653 | &eht_src_addr); | |
654 | if (!eht_set) | |
655 | continue; | |
adc47037 | 656 | mod_timer(&eht_set->timer, jiffies + br_multicast_lmqt(brmctx)); |
ddc255d9 NA |
657 | } |
658 | } | |
659 | ||
660 | return changed; | |
661 | } | |
662 | ||
adc47037 NA |
663 | static bool br_multicast_eht_inc(const struct net_bridge_mcast *brmctx, |
664 | struct net_bridge_port_group *pg, | |
ddc255d9 NA |
665 | union net_bridge_eht_addr *h_addr, |
666 | void *srcs, | |
667 | u32 nsrcs, | |
668 | size_t addr_size, | |
669 | bool to_report) | |
670 | { | |
c9739016 NA |
671 | bool changed; |
672 | ||
adc47037 | 673 | changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
c9739016 | 674 | MCAST_INCLUDE, to_report); |
adc47037 | 675 | br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_INCLUDE); |
c9739016 NA |
676 | |
677 | return changed; | |
ddc255d9 NA |
678 | } |
679 | ||
adc47037 NA |
680 | static bool br_multicast_eht_exc(const struct net_bridge_mcast *brmctx, |
681 | struct net_bridge_port_group *pg, | |
ddc255d9 NA |
682 | union net_bridge_eht_addr *h_addr, |
683 | void *srcs, | |
684 | u32 nsrcs, | |
685 | size_t addr_size, | |
686 | bool to_report) | |
687 | { | |
c9739016 NA |
688 | bool changed; |
689 | ||
adc47037 | 690 | changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
c9739016 | 691 | MCAST_EXCLUDE, to_report); |
adc47037 | 692 | br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_EXCLUDE); |
c9739016 NA |
693 | |
694 | return changed; | |
ddc255d9 NA |
695 | } |
696 | ||
adc47037 NA |
697 | static bool __eht_ip4_handle(const struct net_bridge_mcast *brmctx, |
698 | struct net_bridge_port_group *pg, | |
474ddb37 NA |
699 | union net_bridge_eht_addr *h_addr, |
700 | void *srcs, | |
701 | u32 nsrcs, | |
702 | int grec_type) | |
703 | { | |
ddc255d9 | 704 | bool changed = false, to_report = false; |
474ddb37 NA |
705 | |
706 | switch (grec_type) { | |
707 | case IGMPV3_ALLOW_NEW_SOURCES: | |
adc47037 NA |
708 | br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs, |
709 | sizeof(__be32)); | |
474ddb37 NA |
710 | break; |
711 | case IGMPV3_BLOCK_OLD_SOURCES: | |
adc47037 | 712 | changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs, |
474ddb37 NA |
713 | sizeof(__be32)); |
714 | break; | |
ddc255d9 NA |
715 | case IGMPV3_CHANGE_TO_INCLUDE: |
716 | to_report = true; | |
717 | fallthrough; | |
718 | case IGMPV3_MODE_IS_INCLUDE: | |
adc47037 | 719 | changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs, |
ddc255d9 NA |
720 | sizeof(__be32), to_report); |
721 | break; | |
722 | case IGMPV3_CHANGE_TO_EXCLUDE: | |
723 | to_report = true; | |
724 | fallthrough; | |
725 | case IGMPV3_MODE_IS_EXCLUDE: | |
adc47037 | 726 | changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs, |
ddc255d9 NA |
727 | sizeof(__be32), to_report); |
728 | break; | |
474ddb37 NA |
729 | } |
730 | ||
731 | return changed; | |
732 | } | |
733 | ||
734 | #if IS_ENABLED(CONFIG_IPV6) | |
adc47037 NA |
735 | static bool __eht_ip6_handle(const struct net_bridge_mcast *brmctx, |
736 | struct net_bridge_port_group *pg, | |
474ddb37 NA |
737 | union net_bridge_eht_addr *h_addr, |
738 | void *srcs, | |
739 | u32 nsrcs, | |
740 | int grec_type) | |
741 | { | |
ddc255d9 | 742 | bool changed = false, to_report = false; |
474ddb37 NA |
743 | |
744 | switch (grec_type) { | |
745 | case MLD2_ALLOW_NEW_SOURCES: | |
adc47037 | 746 | br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs, |
474ddb37 NA |
747 | sizeof(struct in6_addr)); |
748 | break; | |
749 | case MLD2_BLOCK_OLD_SOURCES: | |
adc47037 | 750 | changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs, |
474ddb37 NA |
751 | sizeof(struct in6_addr)); |
752 | break; | |
ddc255d9 NA |
753 | case MLD2_CHANGE_TO_INCLUDE: |
754 | to_report = true; | |
755 | fallthrough; | |
756 | case MLD2_MODE_IS_INCLUDE: | |
adc47037 | 757 | changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs, |
ddc255d9 NA |
758 | sizeof(struct in6_addr), |
759 | to_report); | |
760 | break; | |
761 | case MLD2_CHANGE_TO_EXCLUDE: | |
762 | to_report = true; | |
763 | fallthrough; | |
764 | case MLD2_MODE_IS_EXCLUDE: | |
adc47037 | 765 | changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs, |
ddc255d9 NA |
766 | sizeof(struct in6_addr), |
767 | to_report); | |
768 | break; | |
474ddb37 NA |
769 | } |
770 | ||
771 | return changed; | |
772 | } | |
773 | #endif | |
774 | ||
775 | /* true means an entry was deleted */ | |
adc47037 NA |
776 | bool br_multicast_eht_handle(const struct net_bridge_mcast *brmctx, |
777 | struct net_bridge_port_group *pg, | |
474ddb37 NA |
778 | void *h_addr, |
779 | void *srcs, | |
780 | u32 nsrcs, | |
781 | size_t addr_size, | |
782 | int grec_type) | |
783 | { | |
784 | bool eht_enabled = !!(pg->key.port->flags & BR_MULTICAST_FAST_LEAVE); | |
785 | union net_bridge_eht_addr eht_host_addr; | |
786 | bool changed = false; | |
787 | ||
788 | if (!eht_enabled) | |
789 | goto out; | |
790 | ||
791 | memset(&eht_host_addr, 0, sizeof(eht_host_addr)); | |
792 | memcpy(&eht_host_addr, h_addr, addr_size); | |
793 | if (addr_size == sizeof(__be32)) | |
adc47037 NA |
794 | changed = __eht_ip4_handle(brmctx, pg, &eht_host_addr, srcs, |
795 | nsrcs, grec_type); | |
474ddb37 NA |
796 | #if IS_ENABLED(CONFIG_IPV6) |
797 | else | |
adc47037 NA |
798 | changed = __eht_ip6_handle(brmctx, pg, &eht_host_addr, srcs, |
799 | nsrcs, grec_type); | |
474ddb37 NA |
800 | #endif |
801 | ||
802 | out: | |
803 | return changed; | |
804 | } | |
2dba407f NA |
805 | |
806 | int br_multicast_eht_set_hosts_limit(struct net_bridge_port *p, | |
807 | u32 eht_hosts_limit) | |
808 | { | |
809 | struct net_bridge *br = p->br; | |
810 | ||
811 | if (!eht_hosts_limit) | |
812 | return -EINVAL; | |
813 | ||
814 | spin_lock_bh(&br->multicast_lock); | |
815 | p->multicast_eht_hosts_limit = eht_hosts_limit; | |
816 | spin_unlock_bh(&br->multicast_lock); | |
817 | ||
818 | return 0; | |
819 | } |