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 | |
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); |
36 | static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx, |
37 | struct net_bridge_port_group *pg, |
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 | |
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(p: h_addr, q: &this->h_addr, size: 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 | |
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 | |
89 | this = rb_entry(node, struct net_bridge_group_eht_set_entry, |
90 | rb_node); |
91 | result = memcmp(p: h_addr, q: &this->h_addr, size: 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; |
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(p: src_addr, q: &this->src_addr, size: 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 | |
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 | |
131 | br_multicast_eht_hosts_dec(pg: eht_host->pg); |
132 | |
133 | rb_erase(&eht_host->rb_node, &eht_host->pg->eht_host_tree); |
134 | RB_CLEAR_NODE(&eht_host->rb_node); |
135 | kfree(objp: eht_host); |
136 | } |
137 | |
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 | |
145 | timer_shutdown_sync(timer: &set_h->timer); |
146 | kfree(objp: 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 | |
157 | timer_shutdown_sync(timer: &eht_set->timer); |
158 | kfree(objp: 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(n: &set_h->host_list); |
169 | memset(&zero_addr, 0, sizeof(zero_addr)); |
170 | if (memcmp(p: &set_h->h_addr, q: &zero_addr, size: sizeof(zero_addr))) |
171 | eht_host->num_entries--; |
172 | hlist_add_head(n: &set_h->mcast_gc.gc_node, h: &set_h->br->mcast_gc_list); |
173 | queue_work(wq: system_long_wq, work: &set_h->br->mcast_gc_work); |
174 | |
175 | if (hlist_empty(h: &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(n: &eht_set->mcast_gc.gc_node, h: &eht_set->br->mcast_gc_list); |
193 | queue_work(wq: system_long_wq, work: &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(lock: &br->multicast_lock); |
214 | if (RB_EMPTY_NODE(&set_h->rb_node) || timer_pending(timer: &set_h->timer)) |
215 | goto out; |
216 | |
217 | br_multicast_del_eht_set_entry(pg: set_h->eht_set->pg, |
218 | src_addr: &set_h->eht_set->src_addr, |
219 | h_addr: &set_h->h_addr); |
220 | out: |
221 | spin_unlock(lock: &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(lock: &br->multicast_lock); |
231 | if (RB_EMPTY_NODE(&eht_set->rb_node) || timer_pending(timer: &eht_set->timer)) |
232 | goto out; |
233 | |
234 | br_multicast_del_eht_set(eht_set); |
235 | out: |
236 | spin_unlock(lock: &br->multicast_lock); |
237 | } |
238 | |
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(p: h_addr, q: &this->h_addr, size: 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 | |
263 | if (br_multicast_eht_hosts_over_limit(pg)) |
264 | return NULL; |
265 | |
266 | eht_host = kzalloc(size: 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(node: &eht_host->rb_node, parent, rb_link: link); |
276 | rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree); |
277 | |
278 | br_multicast_eht_hosts_inc(pg); |
279 | |
280 | return eht_host; |
281 | } |
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(p: &eht_host->h_addr, q: &this->h_addr, |
299 | size: 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(size: 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(n: &set_h->host_list, h: &eht_host->set_entries); |
326 | rb_link_node(node: &set_h->rb_node, parent, rb_link: 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(p: src_addr, q: &this->src_addr, size: 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(size: 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(node: &eht_set->rb_node, parent, rb_link: link); |
372 | rb_insert_color(&eht_set->rb_node, &pg->eht_set_tree); |
373 | |
374 | return eht_set; |
375 | } |
376 | |
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 | |
392 | static void br_eht_convert_host_filter_mode(const struct net_bridge_mcast *brmctx, |
393 | struct net_bridge_port_group *pg, |
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, src_addr: &zero_addr, h_addr); |
408 | break; |
409 | case MCAST_EXCLUDE: |
410 | br_multicast_create_eht_set_entry(brmctx, pg, src_addr: &zero_addr, |
411 | h_addr, MCAST_EXCLUDE, |
412 | allow_zero_src: true); |
413 | break; |
414 | } |
415 | } |
416 | |
417 | static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx, |
418 | struct net_bridge_port_group *pg, |
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(p: src_addr, q: &zero_addr, size: 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 | |
447 | mod_timer(timer: &set_h->timer, expires: jiffies + br_multicast_gmi(brmctx)); |
448 | mod_timer(timer: &eht_set->timer, expires: jiffies + br_multicast_gmi(brmctx)); |
449 | |
450 | return; |
451 | |
452 | fail_set_entry: |
453 | if (hlist_empty(h: &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 | } |
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(pg: set_h->eht_set->pg, |
500 | src_addr: &set_h->eht_set->src_addr, |
501 | h_addr: &set_h->h_addr); |
502 | } |
503 | |
504 | /* create new set entries from reports */ |
505 | static void __eht_create_set_entries(const struct net_bridge_mcast *brmctx, |
506 | struct net_bridge_port_group *pg, |
507 | union net_bridge_eht_addr *h_addr, |
508 | void *srcs, |
509 | u32 nsrcs, |
510 | size_t addr_size, |
511 | int filter_mode) |
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); |
519 | br_multicast_create_eht_set_entry(brmctx, pg, src_addr: &eht_src_addr, |
520 | h_addr, filter_mode, |
521 | allow_zero_src: false); |
522 | } |
523 | } |
524 | |
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) |
531 | { |
532 | union net_bridge_eht_addr eht_src_addr; |
533 | struct net_bridge_group_src *src_ent; |
534 | bool changed = false; |
535 | struct br_ip src_ip; |
536 | u32 src_idx; |
537 | |
538 | memset(&eht_src_addr, 0, sizeof(eht_src_addr)); |
539 | memset(&src_ip, 0, sizeof(src_ip)); |
540 | src_ip.proto = pg->key.addr.proto; |
541 | for (src_idx = 0; src_idx < nsrcs; src_idx++) { |
542 | memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size); |
543 | if (!br_multicast_del_eht_set_entry(pg, src_addr: &eht_src_addr, h_addr)) |
544 | continue; |
545 | memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size); |
546 | src_ent = br_multicast_find_group_src(pg, ip: &src_ip); |
547 | if (!src_ent) |
548 | continue; |
549 | br_multicast_del_group_src(src: src_ent, fastleave: true); |
550 | changed = true; |
551 | } |
552 | |
553 | return changed; |
554 | } |
555 | |
556 | static bool br_multicast_eht_allow(const struct net_bridge_mcast *brmctx, |
557 | struct net_bridge_port_group *pg, |
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: |
567 | __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, |
568 | addr_size, MCAST_INCLUDE); |
569 | break; |
570 | case MCAST_EXCLUDE: |
571 | changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs, |
572 | addr_size); |
573 | break; |
574 | } |
575 | |
576 | return changed; |
577 | } |
578 | |
579 | static bool br_multicast_eht_block(const struct net_bridge_mcast *brmctx, |
580 | struct net_bridge_port_group *pg, |
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: |
590 | changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs, |
591 | addr_size); |
592 | break; |
593 | case MCAST_EXCLUDE: |
594 | __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
595 | MCAST_EXCLUDE); |
596 | break; |
597 | } |
598 | |
599 | return changed; |
600 | } |
601 | |
602 | /* flush_entries is true when changing mode */ |
603 | static bool __eht_inc_exc(const struct net_bridge_mcast *brmctx, |
604 | struct net_bridge_port_group *pg, |
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; |
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); |
622 | __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
623 | filter_mode); |
624 | /* we can be missing sets only if we've deleted some entries */ |
625 | if (flush_entries) { |
626 | struct net_bridge_group_eht_set *eht_set; |
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: &src_ent->addr, |
632 | dest: &eht_src_addr); |
633 | if (!br_multicast_eht_set_lookup(pg, src_addr: &eht_src_addr)) { |
634 | br_multicast_del_group_src(src: src_ent, fastleave: true); |
635 | changed = true; |
636 | continue; |
637 | } |
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 | src_addr: &eht_src_addr); |
654 | if (!eht_set) |
655 | continue; |
656 | mod_timer(timer: &eht_set->timer, expires: jiffies + br_multicast_lmqt(brmctx)); |
657 | } |
658 | } |
659 | |
660 | return changed; |
661 | } |
662 | |
663 | static bool br_multicast_eht_inc(const struct net_bridge_mcast *brmctx, |
664 | struct net_bridge_port_group *pg, |
665 | union net_bridge_eht_addr *h_addr, |
666 | void *srcs, |
667 | u32 nsrcs, |
668 | size_t addr_size, |
669 | bool to_report) |
670 | { |
671 | bool changed; |
672 | |
673 | changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
674 | MCAST_INCLUDE, to_report); |
675 | br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_INCLUDE); |
676 | |
677 | return changed; |
678 | } |
679 | |
680 | static bool br_multicast_eht_exc(const struct net_bridge_mcast *brmctx, |
681 | struct net_bridge_port_group *pg, |
682 | union net_bridge_eht_addr *h_addr, |
683 | void *srcs, |
684 | u32 nsrcs, |
685 | size_t addr_size, |
686 | bool to_report) |
687 | { |
688 | bool changed; |
689 | |
690 | changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size, |
691 | MCAST_EXCLUDE, to_report); |
692 | br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_EXCLUDE); |
693 | |
694 | return changed; |
695 | } |
696 | |
697 | static bool __eht_ip4_handle(const struct net_bridge_mcast *brmctx, |
698 | struct net_bridge_port_group *pg, |
699 | union net_bridge_eht_addr *h_addr, |
700 | void *srcs, |
701 | u32 nsrcs, |
702 | int grec_type) |
703 | { |
704 | bool changed = false, to_report = false; |
705 | |
706 | switch (grec_type) { |
707 | case IGMPV3_ALLOW_NEW_SOURCES: |
708 | br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs, |
709 | addr_size: sizeof(__be32)); |
710 | break; |
711 | case IGMPV3_BLOCK_OLD_SOURCES: |
712 | changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs, |
713 | addr_size: sizeof(__be32)); |
714 | break; |
715 | case IGMPV3_CHANGE_TO_INCLUDE: |
716 | to_report = true; |
717 | fallthrough; |
718 | case IGMPV3_MODE_IS_INCLUDE: |
719 | changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs, |
720 | addr_size: sizeof(__be32), to_report); |
721 | break; |
722 | case IGMPV3_CHANGE_TO_EXCLUDE: |
723 | to_report = true; |
724 | fallthrough; |
725 | case IGMPV3_MODE_IS_EXCLUDE: |
726 | changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs, |
727 | addr_size: sizeof(__be32), to_report); |
728 | break; |
729 | } |
730 | |
731 | return changed; |
732 | } |
733 | |
734 | #if IS_ENABLED(CONFIG_IPV6) |
735 | static bool __eht_ip6_handle(const struct net_bridge_mcast *brmctx, |
736 | struct net_bridge_port_group *pg, |
737 | union net_bridge_eht_addr *h_addr, |
738 | void *srcs, |
739 | u32 nsrcs, |
740 | int grec_type) |
741 | { |
742 | bool changed = false, to_report = false; |
743 | |
744 | switch (grec_type) { |
745 | case MLD2_ALLOW_NEW_SOURCES: |
746 | br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs, |
747 | addr_size: sizeof(struct in6_addr)); |
748 | break; |
749 | case MLD2_BLOCK_OLD_SOURCES: |
750 | changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs, |
751 | addr_size: sizeof(struct in6_addr)); |
752 | break; |
753 | case MLD2_CHANGE_TO_INCLUDE: |
754 | to_report = true; |
755 | fallthrough; |
756 | case MLD2_MODE_IS_INCLUDE: |
757 | changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs, |
758 | addr_size: 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: |
765 | changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs, |
766 | addr_size: sizeof(struct in6_addr), |
767 | to_report); |
768 | break; |
769 | } |
770 | |
771 | return changed; |
772 | } |
773 | #endif |
774 | |
775 | /* true means an entry was deleted */ |
776 | bool br_multicast_eht_handle(const struct net_bridge_mcast *brmctx, |
777 | struct net_bridge_port_group *pg, |
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)) |
794 | changed = __eht_ip4_handle(brmctx, pg, h_addr: &eht_host_addr, srcs, |
795 | nsrcs, grec_type); |
796 | #if IS_ENABLED(CONFIG_IPV6) |
797 | else |
798 | changed = __eht_ip6_handle(brmctx, pg, h_addr: &eht_host_addr, srcs, |
799 | nsrcs, grec_type); |
800 | #endif |
801 | |
802 | out: |
803 | return changed; |
804 | } |
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(lock: &br->multicast_lock); |
815 | p->multicast_eht_hosts_limit = eht_hosts_limit; |
816 | spin_unlock_bh(lock: &br->multicast_lock); |
817 | |
818 | return 0; |
819 | } |
820 | |