]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv3.c
Merge pull request #4743 from opensourcerouting/7.1/ospfd-default-originate
[mirror_frr.git] / pimd / pim_igmpv3.c
1 /*
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <zebra.h>
21 #include "log.h"
22 #include "memory.h"
23 #include "if.h"
24 #include "lib_errors.h"
25
26 #include "pimd.h"
27 #include "pim_iface.h"
28 #include "pim_igmp.h"
29 #include "pim_igmpv3.h"
30 #include "pim_str.h"
31 #include "pim_util.h"
32 #include "pim_time.h"
33 #include "pim_zebra.h"
34 #include "pim_oil.h"
35
36 static void group_retransmit_timer_on(struct igmp_group *group);
37 static long igmp_group_timer_remain_msec(struct igmp_group *group);
38 static long igmp_source_timer_remain_msec(struct igmp_source *source);
39 static void group_query_send(struct igmp_group *group);
40 static void source_query_send_by_flag(struct igmp_group *group,
41 int num_sources_tosend);
42
43 static void on_trace(const char *label, struct interface *ifp,
44 struct in_addr from, struct in_addr group_addr,
45 int num_sources, struct in_addr *sources)
46 {
47 if (PIM_DEBUG_IGMP_TRACE) {
48 char from_str[INET_ADDRSTRLEN];
49 char group_str[INET_ADDRSTRLEN];
50
51 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
52 pim_inet4_dump("<group?>", group_addr, group_str,
53 sizeof(group_str));
54
55 zlog_debug("%s: from %s on %s: group=%s sources=%d", label,
56 from_str, ifp->name, group_str, num_sources);
57 }
58 }
59
60 void igmp_group_reset_gmi(struct igmp_group *group)
61 {
62 long group_membership_interval_msec;
63 struct pim_interface *pim_ifp;
64 struct igmp_sock *igmp;
65 struct interface *ifp;
66
67 igmp = group->group_igmp_sock;
68 ifp = igmp->interface;
69 pim_ifp = ifp->info;
70
71 /*
72 RFC 3376: 8.4. Group Membership Interval
73
74 The Group Membership Interval is the amount of time that must pass
75 before a multicast router decides there are no more members of a
76 group or a particular source on a network.
77
78 This value MUST be ((the Robustness Variable) times (the Query
79 Interval)) plus (one Query Response Interval).
80
81 group_membership_interval_msec = querier_robustness_variable *
82 (1000 * querier_query_interval) +
83 100 * query_response_interval_dsec;
84 */
85 group_membership_interval_msec = PIM_IGMP_GMI_MSEC(
86 igmp->querier_robustness_variable, igmp->querier_query_interval,
87 pim_ifp->igmp_query_max_response_time_dsec);
88
89 if (PIM_DEBUG_IGMP_TRACE) {
90 char group_str[INET_ADDRSTRLEN];
91 pim_inet4_dump("<group?>", group->group_addr, group_str,
92 sizeof(group_str));
93 zlog_debug(
94 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
95 group_str, group_membership_interval_msec / 1000,
96 group_membership_interval_msec % 1000, ifp->name);
97 }
98
99 /*
100 RFC 3376: 6.2.2. Definition of Group Timers
101
102 The group timer is only used when a group is in EXCLUDE mode and
103 it represents the time for the *filter-mode* of the group to
104 expire and switch to INCLUDE mode.
105 */
106 zassert(group->group_filtermode_isexcl);
107
108 igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
109 }
110
111 static int igmp_source_timer(struct thread *t)
112 {
113 struct igmp_source *source;
114 struct igmp_group *group;
115
116 source = THREAD_ARG(t);
117
118 group = source->source_group;
119
120 if (PIM_DEBUG_IGMP_TRACE) {
121 char group_str[INET_ADDRSTRLEN];
122 char source_str[INET_ADDRSTRLEN];
123 pim_inet4_dump("<group?>", group->group_addr, group_str,
124 sizeof(group_str));
125 pim_inet4_dump("<source?>", source->source_addr, source_str,
126 sizeof(source_str));
127 zlog_debug(
128 "%s: Source timer expired for group %s source %s on %s",
129 __PRETTY_FUNCTION__, group_str, source_str,
130 group->group_igmp_sock->interface->name);
131 }
132
133 /*
134 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
135
136 Group
137 Filter-Mode Source Timer Value Action
138 ----------- ------------------ ------
139 INCLUDE TIMER == 0 Suggest to stop forwarding
140 traffic from source and
141 remove source record. If
142 there are no more source
143 records for the group, delete
144 group record.
145
146 EXCLUDE TIMER == 0 Suggest to not forward
147 traffic from source
148 (DO NOT remove record)
149
150 Source timer switched from (T > 0) to (T == 0): disable forwarding.
151 */
152
153 if (group->group_filtermode_isexcl) {
154 /* EXCLUDE mode */
155
156 igmp_source_forward_stop(source);
157 } else {
158 /* INCLUDE mode */
159
160 /* igmp_source_delete() will stop forwarding source */
161 igmp_source_delete(source);
162
163 /*
164 If there are no more source records for the group, delete
165 group
166 record.
167 */
168 if (!listcount(group->group_source_list)) {
169 igmp_group_delete_empty_include(group);
170 }
171 }
172
173 return 0;
174 }
175
176 static void source_timer_off(struct igmp_group *group,
177 struct igmp_source *source)
178 {
179 if (!source->t_source_timer)
180 return;
181
182 if (PIM_DEBUG_IGMP_TRACE) {
183 char group_str[INET_ADDRSTRLEN];
184 char source_str[INET_ADDRSTRLEN];
185 pim_inet4_dump("<group?>", group->group_addr, group_str,
186 sizeof(group_str));
187 pim_inet4_dump("<source?>", source->source_addr, source_str,
188 sizeof(source_str));
189 zlog_debug(
190 "Cancelling TIMER event for group %s source %s on %s",
191 group_str, source_str,
192 group->group_igmp_sock->interface->name);
193 }
194
195 THREAD_OFF(source->t_source_timer);
196 }
197
198 static void igmp_source_timer_on(struct igmp_group *group,
199 struct igmp_source *source, long interval_msec)
200 {
201 source_timer_off(group, source);
202 struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info;
203
204 if (PIM_DEBUG_IGMP_EVENTS) {
205 char group_str[INET_ADDRSTRLEN];
206 char source_str[INET_ADDRSTRLEN];
207 pim_inet4_dump("<group?>", group->group_addr, group_str,
208 sizeof(group_str));
209 pim_inet4_dump("<source?>", source->source_addr, source_str,
210 sizeof(source_str));
211 zlog_debug(
212 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
213 interval_msec / 1000, interval_msec % 1000, group_str,
214 source_str, group->group_igmp_sock->interface->name);
215 }
216
217 thread_add_timer_msec(router->master, igmp_source_timer, source,
218 interval_msec, &source->t_source_timer);
219
220 /*
221 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
222
223 Source timer switched from (T == 0) to (T > 0): enable forwarding.
224 */
225 igmp_source_forward_start(pim_ifp->pim, source);
226 }
227
228 void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group,
229 struct igmp_source *source)
230 {
231 long group_membership_interval_msec;
232 struct pim_interface *pim_ifp;
233 struct interface *ifp;
234
235 ifp = igmp->interface;
236 pim_ifp = ifp->info;
237
238 group_membership_interval_msec = PIM_IGMP_GMI_MSEC(
239 igmp->querier_robustness_variable, igmp->querier_query_interval,
240 pim_ifp->igmp_query_max_response_time_dsec);
241
242 if (PIM_DEBUG_IGMP_TRACE) {
243 char group_str[INET_ADDRSTRLEN];
244 char source_str[INET_ADDRSTRLEN];
245
246 pim_inet4_dump("<group?>", group->group_addr, group_str,
247 sizeof(group_str));
248 pim_inet4_dump("<source?>", source->source_addr, source_str,
249 sizeof(source_str));
250
251 zlog_debug(
252 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
253 source_str, group_membership_interval_msec / 1000,
254 group_membership_interval_msec % 1000, group_str,
255 ifp->name);
256 }
257
258 igmp_source_timer_on(group, source, group_membership_interval_msec);
259 }
260
261 static void source_mark_delete_flag(struct igmp_group *group)
262 {
263 struct listnode *src_node;
264 struct igmp_source *src;
265
266 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
267 IGMP_SOURCE_DO_DELETE(src->source_flags);
268 }
269 }
270
271 static void source_mark_send_flag(struct igmp_group *group)
272 {
273 struct listnode *src_node;
274 struct igmp_source *src;
275
276 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
277 IGMP_SOURCE_DO_SEND(src->source_flags);
278 }
279 }
280
281 static int source_mark_send_flag_by_timer(struct igmp_group *group)
282 {
283 struct listnode *src_node;
284 struct igmp_source *src;
285 int num_marked_sources = 0;
286
287 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
288 /* Is source timer running? */
289 if (src->t_source_timer) {
290 IGMP_SOURCE_DO_SEND(src->source_flags);
291 ++num_marked_sources;
292 } else {
293 IGMP_SOURCE_DONT_SEND(src->source_flags);
294 }
295 }
296
297 return num_marked_sources;
298 }
299
300 static void source_clear_send_flag(struct list *source_list)
301 {
302 struct listnode *src_node;
303 struct igmp_source *src;
304
305 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
306 IGMP_SOURCE_DONT_SEND(src->source_flags);
307 }
308 }
309
310 /*
311 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
312 */
313 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
314 {
315 struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info;
316
317 zassert(group->group_filtermode_isexcl);
318
319 if (listcount(group->group_source_list) < 1) {
320 igmp_anysource_forward_start(pim_ifp->pim, group);
321 }
322 }
323
324 void igmp_source_free(struct igmp_source *source)
325 {
326 /* make sure there is no source timer running */
327 zassert(!source->t_source_timer);
328
329 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
330 }
331
332 static void source_channel_oil_detach(struct igmp_source *source)
333 {
334 if (source->source_channel_oil) {
335 pim_channel_oil_del(source->source_channel_oil);
336 source->source_channel_oil = NULL;
337 }
338 }
339
340 /*
341 igmp_source_delete: stop fowarding, and delete the source
342 igmp_source_forward_stop: stop fowarding, but keep the source
343 */
344 void igmp_source_delete(struct igmp_source *source)
345 {
346 struct igmp_group *group;
347 struct in_addr src;
348
349 group = source->source_group;
350
351 if (PIM_DEBUG_IGMP_TRACE) {
352 char group_str[INET_ADDRSTRLEN];
353 char source_str[INET_ADDRSTRLEN];
354 pim_inet4_dump("<group?>", group->group_addr, group_str,
355 sizeof(group_str));
356 pim_inet4_dump("<source?>", source->source_addr, source_str,
357 sizeof(source_str));
358 zlog_debug(
359 "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
360 source_str, group_str, group->group_igmp_sock->fd,
361 group->group_igmp_sock->interface->name,
362 source->source_channel_oil
363 ? source->source_channel_oil->oil_ref_count
364 : 0);
365 }
366
367 source_timer_off(group, source);
368 igmp_source_forward_stop(source);
369
370 /* sanity check that forwarding has been disabled */
371 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
372 char group_str[INET_ADDRSTRLEN];
373 char source_str[INET_ADDRSTRLEN];
374 pim_inet4_dump("<group?>", group->group_addr, group_str,
375 sizeof(group_str));
376 pim_inet4_dump("<source?>", source->source_addr, source_str,
377 sizeof(source_str));
378 zlog_warn(
379 "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
380 __PRETTY_FUNCTION__, source_str, group_str,
381 group->group_igmp_sock->fd,
382 group->group_igmp_sock->interface->name);
383 /* warning only */
384 }
385
386 source_channel_oil_detach(source);
387
388 /*
389 notice that listnode_delete() can't be moved
390 into igmp_source_free() because the later is
391 called by list_delete_all_node()
392 */
393 listnode_delete(group->group_source_list, source);
394
395 src.s_addr = source->source_addr.s_addr;
396 igmp_source_free(source);
397
398 /* Group source list is empty and current source is * then
399 *,G group going away so do not trigger start */
400 if (group->group_filtermode_isexcl
401 && (listcount(group->group_source_list) != 0)
402 && src.s_addr != INADDR_ANY) {
403 group_exclude_fwd_anysrc_ifempty(group);
404 }
405 }
406
407 static void source_delete_by_flag(struct list *source_list)
408 {
409 struct listnode *src_node;
410 struct listnode *src_nextnode;
411 struct igmp_source *src;
412
413 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
414 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
415 igmp_source_delete(src);
416 }
417
418 void igmp_source_delete_expired(struct list *source_list)
419 {
420 struct listnode *src_node;
421 struct listnode *src_nextnode;
422 struct igmp_source *src;
423
424 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
425 if (!src->t_source_timer)
426 igmp_source_delete(src);
427 }
428
429 struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
430 struct in_addr src_addr)
431 {
432 struct listnode *src_node;
433 struct igmp_source *src;
434
435 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
436 if (src_addr.s_addr == src->source_addr.s_addr)
437 return src;
438
439 return 0;
440 }
441
442 struct igmp_source *source_new(struct igmp_group *group,
443 struct in_addr src_addr)
444 {
445 struct igmp_source *src;
446
447 if (PIM_DEBUG_IGMP_TRACE) {
448 char group_str[INET_ADDRSTRLEN];
449 char source_str[INET_ADDRSTRLEN];
450 pim_inet4_dump("<group?>", group->group_addr, group_str,
451 sizeof(group_str));
452 pim_inet4_dump("<source?>", src_addr, source_str,
453 sizeof(source_str));
454 zlog_debug(
455 "Creating new IGMP source %s for group %s on socket %d interface %s",
456 source_str, group_str, group->group_igmp_sock->fd,
457 group->group_igmp_sock->interface->name);
458 }
459
460 src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
461
462 src->t_source_timer = NULL;
463 src->source_group = group; /* back pointer */
464 src->source_addr = src_addr;
465 src->source_creation = pim_time_monotonic_sec();
466 src->source_flags = 0;
467 src->source_query_retransmit_count = 0;
468 src->source_channel_oil = NULL;
469
470 listnode_add(group->group_source_list, src);
471
472 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
473 igmp_anysource_forward_stop(group);
474
475 return src;
476 }
477
478 static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
479 struct igmp_group *group,
480 struct in_addr src_addr)
481 {
482 struct igmp_source *src;
483
484 src = igmp_find_source_by_addr(group, src_addr);
485 if (src) {
486 return src;
487 }
488
489 src = source_new(group, src_addr);
490
491 return src;
492 }
493
494 static void allow(struct igmp_sock *igmp, struct in_addr from,
495 struct in_addr group_addr, int num_sources,
496 struct in_addr *sources)
497 {
498 struct igmp_source *source;
499 struct igmp_group *group;
500 int i;
501
502 /* non-existant group is created as INCLUDE {empty} */
503 group = igmp_add_group_by_addr(igmp, group_addr);
504 if (!group) {
505 return;
506 }
507
508 /* scan received sources */
509 for (i = 0; i < num_sources; ++i) {
510 struct in_addr *src_addr;
511
512 src_addr = sources + i;
513
514 source = add_source_by_addr(igmp, group, *src_addr);
515 if (!source) {
516 continue;
517 }
518
519 /*
520 RFC 3376: 6.4.1. Reception of Current-State Records
521
522 When receiving IS_IN reports for groups in EXCLUDE mode is
523 sources should be moved from set with (timers = 0) to set with
524 (timers > 0).
525
526 igmp_source_reset_gmi() below, resetting the source timers to
527 GMI, accomplishes this.
528 */
529 igmp_source_reset_gmi(igmp, group, source);
530
531 } /* scan received sources */
532
533 if ((num_sources == 0) && (group->group_filtermode_isexcl)
534 && (listcount(group->group_source_list) == 1)) {
535 struct in_addr star = {.s_addr = INADDR_ANY};
536
537 source = igmp_find_source_by_addr(group, star);
538 if (source)
539 igmp_source_reset_gmi(igmp, group, source);
540 }
541 }
542
543 void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
544 struct in_addr group_addr, int num_sources,
545 struct in_addr *sources)
546 {
547 on_trace(__PRETTY_FUNCTION__, igmp->interface, from, group_addr,
548 num_sources, sources);
549
550 allow(igmp, from, group_addr, num_sources, sources);
551 }
552
553 static void isex_excl(struct igmp_group *group, int num_sources,
554 struct in_addr *sources)
555 {
556 struct igmp_source *source;
557 int i;
558
559 /* EXCLUDE mode */
560 zassert(group->group_filtermode_isexcl);
561
562 /* E.1: set deletion flag for known sources (X,Y) */
563 source_mark_delete_flag(group);
564
565 /* scan received sources (A) */
566 for (i = 0; i < num_sources; ++i) {
567 struct in_addr *src_addr;
568
569 src_addr = sources + i;
570
571 /* E.2: lookup reported source from (A) in (X,Y) */
572 source = igmp_find_source_by_addr(group, *src_addr);
573 if (source) {
574 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
575 IGMP_SOURCE_DONT_DELETE(source->source_flags);
576 } else {
577 /* E.4: if not found, create source with timer=GMI:
578 * (A-X-Y) */
579 source = source_new(group, *src_addr);
580 zassert(!source->t_source_timer); /* timer == 0 */
581 igmp_source_reset_gmi(group->group_igmp_sock, group,
582 source);
583 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
584 }
585
586 } /* scan received sources */
587
588 /*
589 * If we are in isexcl mode and num_sources == 0
590 * than that means we have a *,g entry that
591 * needs to be handled
592 */
593 if (group->group_filtermode_isexcl && num_sources == 0) {
594 struct in_addr star = {.s_addr = INADDR_ANY};
595 source = igmp_find_source_by_addr(group, star);
596 if (source) {
597 IGMP_SOURCE_DONT_DELETE(source->source_flags);
598 igmp_source_reset_gmi(group->group_igmp_sock, group,
599 source);
600 }
601 }
602
603 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
604 source_delete_by_flag(group->group_source_list);
605 }
606
607 static void isex_incl(struct igmp_group *group, int num_sources,
608 struct in_addr *sources)
609 {
610 int i;
611
612 /* INCLUDE mode */
613 zassert(!group->group_filtermode_isexcl);
614
615 /* I.1: set deletion flag for known sources (A) */
616 source_mark_delete_flag(group);
617
618 /* scan received sources (B) */
619 for (i = 0; i < num_sources; ++i) {
620 struct igmp_source *source;
621 struct in_addr *src_addr;
622
623 src_addr = sources + i;
624
625 /* I.2: lookup reported source (B) */
626 source = igmp_find_source_by_addr(group, *src_addr);
627 if (source) {
628 /* I.3: if found, clear deletion flag (A*B) */
629 IGMP_SOURCE_DONT_DELETE(source->source_flags);
630 } else {
631 /* I.4: if not found, create source with timer=0 (B-A)
632 */
633 source = source_new(group, *src_addr);
634 zassert(!source->t_source_timer); /* (B-A) timer=0 */
635 }
636
637 } /* scan received sources */
638
639 /* I.5: delete all sources marked with deletion flag (A-B) */
640 source_delete_by_flag(group->group_source_list);
641
642 group->group_filtermode_isexcl = 1; /* boolean=true */
643
644 zassert(group->group_filtermode_isexcl);
645
646 group_exclude_fwd_anysrc_ifempty(group);
647 }
648
649 void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
650 struct in_addr group_addr, int num_sources,
651 struct in_addr *sources, int from_igmp_v2_report)
652 {
653 struct interface *ifp = igmp->interface;
654 struct igmp_group *group;
655
656 on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources,
657 sources);
658
659 if (pim_is_group_filtered(ifp->info, &group_addr))
660 return;
661
662 /* non-existant group is created as INCLUDE {empty} */
663 group = igmp_add_group_by_addr(igmp, group_addr);
664 if (!group) {
665 return;
666 }
667
668 /* So we can display how we learned the group in our show command output
669 */
670 if (from_igmp_v2_report)
671 group->igmp_version = 2;
672
673 if (group->group_filtermode_isexcl) {
674 /* EXCLUDE mode */
675 isex_excl(group, num_sources, sources);
676 } else {
677 /* INCLUDE mode */
678 isex_incl(group, num_sources, sources);
679 zassert(group->group_filtermode_isexcl);
680 }
681
682 zassert(group->group_filtermode_isexcl);
683
684 igmp_group_reset_gmi(group);
685 }
686
687 static void toin_incl(struct igmp_group *group, int num_sources,
688 struct in_addr *sources)
689 {
690 struct igmp_sock *igmp = group->group_igmp_sock;
691 int num_sources_tosend = listcount(group->group_source_list);
692 int i;
693
694 /* Set SEND flag for all known sources (A) */
695 source_mark_send_flag(group);
696
697 /* Scan received sources (B) */
698 for (i = 0; i < num_sources; ++i) {
699 struct igmp_source *source;
700 struct in_addr *src_addr;
701
702 src_addr = sources + i;
703
704 /* Lookup reported source (B) */
705 source = igmp_find_source_by_addr(group, *src_addr);
706 if (source) {
707 /* If found, clear SEND flag (A*B) */
708 IGMP_SOURCE_DONT_SEND(source->source_flags);
709 --num_sources_tosend;
710 } else {
711 /* If not found, create new source */
712 source = source_new(group, *src_addr);
713 }
714
715 /* (B)=GMI */
716 igmp_source_reset_gmi(igmp, group, source);
717 }
718
719 /* Send sources marked with SEND flag: Q(G,A-B) */
720 if (num_sources_tosend > 0) {
721 source_query_send_by_flag(group, num_sources_tosend);
722 }
723 }
724
725 static void toin_excl(struct igmp_group *group, int num_sources,
726 struct in_addr *sources)
727 {
728 struct igmp_sock *igmp = group->group_igmp_sock;
729 int num_sources_tosend;
730 int i;
731
732 /* Set SEND flag for X (sources with timer > 0) */
733 num_sources_tosend = source_mark_send_flag_by_timer(group);
734
735 /* Scan received sources (A) */
736 for (i = 0; i < num_sources; ++i) {
737 struct igmp_source *source;
738 struct in_addr *src_addr;
739
740 src_addr = sources + i;
741
742 /* Lookup reported source (A) */
743 source = igmp_find_source_by_addr(group, *src_addr);
744 if (source) {
745 if (source->t_source_timer) {
746 /* If found and timer running, clear SEND flag
747 * (X*A) */
748 IGMP_SOURCE_DONT_SEND(source->source_flags);
749 --num_sources_tosend;
750 }
751 } else {
752 /* If not found, create new source */
753 source = source_new(group, *src_addr);
754 }
755
756 /* (A)=GMI */
757 igmp_source_reset_gmi(igmp, group, source);
758 }
759
760 /* Send sources marked with SEND flag: Q(G,X-A) */
761 if (num_sources_tosend > 0) {
762 source_query_send_by_flag(group, num_sources_tosend);
763 }
764
765 /* Send Q(G) */
766 group_query_send(group);
767 }
768
769 void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
770 struct in_addr group_addr, int num_sources,
771 struct in_addr *sources)
772 {
773 struct interface *ifp = igmp->interface;
774 struct igmp_group *group;
775
776 on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources,
777 sources);
778
779 /*
780 * If the requested filter mode is INCLUDE *and* the requested source
781 * list is empty, then the entry corresponding to the requested
782 * interface and multicast address is deleted if present. If no such
783 * entry is present, the request is ignored.
784 */
785 if (num_sources) {
786 /* non-existant group is created as INCLUDE {empty} */
787 group = igmp_add_group_by_addr(igmp, group_addr);
788 if (!group) {
789 return;
790 }
791 } else {
792 group = find_group_by_addr(igmp, group_addr);
793 if (!group)
794 return;
795 }
796
797 if (group->group_filtermode_isexcl) {
798 /* EXCLUDE mode */
799 toin_excl(group, num_sources, sources);
800 } else {
801 /* INCLUDE mode */
802 toin_incl(group, num_sources, sources);
803 }
804 }
805
806 static void toex_incl(struct igmp_group *group, int num_sources,
807 struct in_addr *sources)
808 {
809 int num_sources_tosend = 0;
810 int i;
811
812 zassert(!group->group_filtermode_isexcl);
813
814 /* Set DELETE flag for all known sources (A) */
815 source_mark_delete_flag(group);
816
817 /* Clear off SEND flag from all known sources (A) */
818 source_clear_send_flag(group->group_source_list);
819
820 /* Scan received sources (B) */
821 for (i = 0; i < num_sources; ++i) {
822 struct igmp_source *source;
823 struct in_addr *src_addr;
824
825 src_addr = sources + i;
826
827 /* Lookup reported source (B) */
828 source = igmp_find_source_by_addr(group, *src_addr);
829 if (source) {
830 /* If found, clear deletion flag: (A*B) */
831 IGMP_SOURCE_DONT_DELETE(source->source_flags);
832 /* and set SEND flag (A*B) */
833 IGMP_SOURCE_DO_SEND(source->source_flags);
834 ++num_sources_tosend;
835 } else {
836 /* If source not found, create source with timer=0:
837 * (B-A)=0 */
838 source = source_new(group, *src_addr);
839 zassert(!source->t_source_timer); /* (B-A) timer=0 */
840 }
841
842 } /* Scan received sources (B) */
843
844 group->group_filtermode_isexcl = 1; /* boolean=true */
845
846 /* Delete all sources marked with DELETE flag (A-B) */
847 source_delete_by_flag(group->group_source_list);
848
849 /* Send sources marked with SEND flag: Q(G,A*B) */
850 if (num_sources_tosend > 0) {
851 source_query_send_by_flag(group, num_sources_tosend);
852 }
853
854 zassert(group->group_filtermode_isexcl);
855
856 group_exclude_fwd_anysrc_ifempty(group);
857 }
858
859 static void toex_excl(struct igmp_group *group, int num_sources,
860 struct in_addr *sources)
861 {
862 int num_sources_tosend = 0;
863 int i;
864
865 /* set DELETE flag for all known sources (X,Y) */
866 source_mark_delete_flag(group);
867
868 /* clear off SEND flag from all known sources (X,Y) */
869 source_clear_send_flag(group->group_source_list);
870
871 if (num_sources == 0) {
872 struct igmp_source *source;
873 struct in_addr any = {.s_addr = INADDR_ANY};
874
875 source = igmp_find_source_by_addr(group, any);
876 if (source)
877 IGMP_SOURCE_DONT_DELETE(source->source_flags);
878 }
879
880 /* scan received sources (A) */
881 for (i = 0; i < num_sources; ++i) {
882 struct igmp_source *source;
883 struct in_addr *src_addr;
884
885 src_addr = sources + i;
886
887 /* lookup reported source (A) in known sources (X,Y) */
888 source = igmp_find_source_by_addr(group, *src_addr);
889 if (source) {
890 /* if found, clear off DELETE flag from reported source
891 * (A) */
892 IGMP_SOURCE_DONT_DELETE(source->source_flags);
893 } else {
894 /* if not found, create source with Group Timer:
895 * (A-X-Y)=Group Timer */
896 long group_timer_msec;
897 source = source_new(group, *src_addr);
898
899 zassert(!source->t_source_timer); /* timer == 0 */
900 group_timer_msec = igmp_group_timer_remain_msec(group);
901 igmp_source_timer_on(group, source, group_timer_msec);
902 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
903
904 /* make sure source is created with DELETE flag unset */
905 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
906 }
907
908 /* make sure reported source has DELETE flag unset */
909 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
910
911 if (source->t_source_timer) {
912 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
913 IGMP_SOURCE_DO_SEND(source->source_flags);
914 ++num_sources_tosend;
915 }
916
917 } /* scan received sources (A) */
918
919 /*
920 delete all sources marked with DELETE flag:
921 Delete (X-A)
922 Delete (Y-A)
923 */
924 source_delete_by_flag(group->group_source_list);
925
926 /* send sources marked with SEND flag: Q(G,A-Y) */
927 if (num_sources_tosend > 0) {
928 source_query_send_by_flag(group, num_sources_tosend);
929 }
930 }
931
932 void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
933 struct in_addr group_addr, int num_sources,
934 struct in_addr *sources)
935 {
936 struct interface *ifp = igmp->interface;
937 struct igmp_group *group;
938
939 on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources,
940 sources);
941
942 /* non-existant group is created as INCLUDE {empty} */
943 group = igmp_add_group_by_addr(igmp, group_addr);
944 if (!group) {
945 return;
946 }
947
948 if (group->group_filtermode_isexcl) {
949 /* EXCLUDE mode */
950 toex_excl(group, num_sources, sources);
951 } else {
952 /* INCLUDE mode */
953 toex_incl(group, num_sources, sources);
954 zassert(group->group_filtermode_isexcl);
955 }
956 zassert(group->group_filtermode_isexcl);
957
958 /* Group Timer=GMI */
959 igmp_group_reset_gmi(group);
960 }
961
962 void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
963 struct in_addr group_addr, int num_sources,
964 struct in_addr *sources)
965 {
966 on_trace(__PRETTY_FUNCTION__, igmp->interface, from, group_addr,
967 num_sources, sources);
968
969 allow(igmp, from, group_addr, num_sources, sources);
970 }
971
972 /*
973 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
974
975 When transmitting a group specific query, if the group timer is
976 larger than LMQT, the "Suppress Router-Side Processing" bit is set
977 in the query message.
978 */
979 static void group_retransmit_group(struct igmp_group *group)
980 {
981 struct igmp_sock *igmp;
982 struct pim_interface *pim_ifp;
983 long lmqc; /* Last Member Query Count */
984 long lmqi_msec; /* Last Member Query Interval */
985 long lmqt_msec; /* Last Member Query Time */
986 int s_flag;
987 int query_buf_size;
988
989 igmp = group->group_igmp_sock;
990 pim_ifp = igmp->interface->info;
991
992 if (pim_ifp->igmp_version == 3) {
993 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
994 } else {
995 query_buf_size = IGMP_V12_MSG_SIZE;
996 }
997
998 char query_buf[query_buf_size];
999
1000 lmqc = igmp->querier_robustness_variable;
1001 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1002 lmqt_msec = lmqc * lmqi_msec;
1003
1004 /*
1005 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1006
1007 When transmitting a group specific query, if the group timer is
1008 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1009 in the query message.
1010 */
1011 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1012
1013 if (PIM_DEBUG_IGMP_TRACE) {
1014 char group_str[INET_ADDRSTRLEN];
1015 pim_inet4_dump("<group?>", group->group_addr, group_str,
1016 sizeof(group_str));
1017 zlog_debug(
1018 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1019 group_str, igmp->interface->name, s_flag,
1020 group->group_specific_query_retransmit_count);
1021 }
1022
1023 /*
1024 RFC3376: 4.1.12. IP Destination Addresses for Queries
1025
1026 Group-Specific and Group-and-Source-Specific Queries are sent with
1027 an IP destination address equal to the multicast address of
1028 interest.
1029 */
1030
1031 igmp_send_query(pim_ifp->igmp_version, group, igmp->fd,
1032 igmp->interface->name, query_buf, sizeof(query_buf),
1033 0 /* num_sources_tosend */,
1034 group->group_addr /* dst_addr */,
1035 group->group_addr /* group_addr */,
1036 pim_ifp->igmp_specific_query_max_response_time_dsec,
1037 s_flag, igmp->querier_robustness_variable,
1038 igmp->querier_query_interval);
1039 }
1040
1041 /*
1042 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1043
1044 When building a group and source specific query for a group G, two
1045 separate query messages are sent for the group. The first one has
1046 the "Suppress Router-Side Processing" bit set and contains all the
1047 sources with retransmission state and timers greater than LMQT. The
1048 second has the "Suppress Router-Side Processing" bit clear and
1049 contains all the sources with retransmission state and timers lower
1050 or equal to LMQT. If either of the two calculated messages does not
1051 contain any sources, then its transmission is suppressed.
1052 */
1053 static int group_retransmit_sources(struct igmp_group *group,
1054 int send_with_sflag_set)
1055 {
1056 struct igmp_sock *igmp;
1057 struct pim_interface *pim_ifp;
1058 long lmqc; /* Last Member Query Count */
1059 long lmqi_msec; /* Last Member Query Interval */
1060 long lmqt_msec; /* Last Member Query Time */
1061 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1062 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1063 int query_buf1_max_sources;
1064 int query_buf2_max_sources;
1065 struct in_addr *source_addr1;
1066 struct in_addr *source_addr2;
1067 int num_sources_tosend1;
1068 int num_sources_tosend2;
1069 struct listnode *src_node;
1070 struct igmp_source *src;
1071 int num_retransmit_sources_left = 0;
1072
1073 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1074 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1075
1076 igmp = group->group_igmp_sock;
1077 pim_ifp = igmp->interface->info;
1078
1079 lmqc = igmp->querier_robustness_variable;
1080 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1081 lmqt_msec = lmqc * lmqi_msec;
1082
1083 /* Scan all group sources */
1084 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1085
1086 /* Source has retransmission state? */
1087 if (src->source_query_retransmit_count < 1)
1088 continue;
1089
1090 if (--src->source_query_retransmit_count > 0) {
1091 ++num_retransmit_sources_left;
1092 }
1093
1094 /* Copy source address into appropriate query buffer */
1095 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1096 *source_addr1 = src->source_addr;
1097 ++source_addr1;
1098 } else {
1099 *source_addr2 = src->source_addr;
1100 ++source_addr2;
1101 }
1102 }
1103
1104 num_sources_tosend1 =
1105 source_addr1
1106 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1107 num_sources_tosend2 =
1108 source_addr2
1109 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1110
1111 if (PIM_DEBUG_IGMP_TRACE) {
1112 char group_str[INET_ADDRSTRLEN];
1113 pim_inet4_dump("<group?>", group->group_addr, group_str,
1114 sizeof(group_str));
1115 zlog_debug(
1116 "retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
1117 group_str, igmp->interface->name, num_sources_tosend1,
1118 num_sources_tosend2, send_with_sflag_set,
1119 num_retransmit_sources_left);
1120 }
1121
1122 if (num_sources_tosend1 > 0) {
1123 /*
1124 Send group-and-source-specific query with s_flag set and all
1125 sources with timers greater than LMQT.
1126 */
1127
1128 if (send_with_sflag_set) {
1129
1130 query_buf1_max_sources =
1131 (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET)
1132 >> 2;
1133 if (num_sources_tosend1 > query_buf1_max_sources) {
1134 char group_str[INET_ADDRSTRLEN];
1135 pim_inet4_dump("<group?>", group->group_addr,
1136 group_str, sizeof(group_str));
1137 zlog_warn(
1138 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1139 __PRETTY_FUNCTION__, group_str,
1140 igmp->interface->name,
1141 num_sources_tosend1, sizeof(query_buf1),
1142 query_buf1_max_sources);
1143 } else {
1144 /*
1145 RFC3376: 4.1.12. IP Destination Addresses for
1146 Queries
1147
1148 Group-Specific and Group-and-Source-Specific
1149 Queries are sent with
1150 an IP destination address equal to the
1151 multicast address of
1152 interest.
1153 */
1154
1155 igmp_send_query(
1156 pim_ifp->igmp_version, group, igmp->fd,
1157 igmp->interface->name, query_buf1,
1158 sizeof(query_buf1), num_sources_tosend1,
1159 group->group_addr, group->group_addr,
1160 pim_ifp->igmp_specific_query_max_response_time_dsec,
1161 1 /* s_flag */,
1162 igmp->querier_robustness_variable,
1163 igmp->querier_query_interval);
1164 }
1165
1166 } /* send_with_sflag_set */
1167 }
1168
1169 if (num_sources_tosend2 > 0) {
1170 /*
1171 Send group-and-source-specific query with s_flag clear and all
1172 sources with timers lower or equal to LMQT.
1173 */
1174
1175 query_buf2_max_sources =
1176 (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1177 if (num_sources_tosend2 > query_buf2_max_sources) {
1178 char group_str[INET_ADDRSTRLEN];
1179 pim_inet4_dump("<group?>", group->group_addr, group_str,
1180 sizeof(group_str));
1181 zlog_warn(
1182 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1183 __PRETTY_FUNCTION__, group_str,
1184 igmp->interface->name, num_sources_tosend2,
1185 sizeof(query_buf2), query_buf2_max_sources);
1186 } else {
1187 /*
1188 RFC3376: 4.1.12. IP Destination Addresses for Queries
1189
1190 Group-Specific and Group-and-Source-Specific Queries
1191 are sent with
1192 an IP destination address equal to the multicast
1193 address of
1194 interest.
1195 */
1196
1197 igmp_send_query(
1198 pim_ifp->igmp_version, group, igmp->fd,
1199 igmp->interface->name, query_buf2,
1200 sizeof(query_buf2), num_sources_tosend2,
1201 group->group_addr, group->group_addr,
1202 pim_ifp->igmp_specific_query_max_response_time_dsec,
1203 0 /* s_flag */,
1204 igmp->querier_robustness_variable,
1205 igmp->querier_query_interval);
1206 }
1207 }
1208
1209 return num_retransmit_sources_left;
1210 }
1211
1212 static int igmp_group_retransmit(struct thread *t)
1213 {
1214 struct igmp_group *group;
1215 int num_retransmit_sources_left;
1216 int send_with_sflag_set; /* boolean */
1217
1218 group = THREAD_ARG(t);
1219
1220 if (PIM_DEBUG_IGMP_TRACE) {
1221 char group_str[INET_ADDRSTRLEN];
1222 pim_inet4_dump("<group?>", group->group_addr, group_str,
1223 sizeof(group_str));
1224 zlog_debug("group_retransmit_timer: group %s on %s", group_str,
1225 group->group_igmp_sock->interface->name);
1226 }
1227
1228 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1229 if (group->group_specific_query_retransmit_count > 0) {
1230
1231 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1232 group_retransmit_group(group);
1233 --group->group_specific_query_retransmit_count;
1234
1235 /*
1236 RFC3376: 6.6.3.2
1237 If a group specific query is scheduled to be transmitted at
1238 the
1239 same time as a group and source specific query for the same
1240 group,
1241 then transmission of the group and source specific message
1242 with the
1243 "Suppress Router-Side Processing" bit set may be suppressed.
1244 */
1245 send_with_sflag_set = 0; /* boolean=false */
1246 } else {
1247 send_with_sflag_set = 1; /* boolean=true */
1248 }
1249
1250 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1251 num_retransmit_sources_left =
1252 group_retransmit_sources(group, send_with_sflag_set);
1253
1254 /*
1255 Keep group retransmit timer running if there is any retransmit
1256 counter pending
1257 */
1258 if ((num_retransmit_sources_left > 0)
1259 || (group->group_specific_query_retransmit_count > 0)) {
1260 group_retransmit_timer_on(group);
1261 }
1262
1263 return 0;
1264 }
1265
1266 /*
1267 group_retransmit_timer_on:
1268 if group retransmit timer isn't running, starts it;
1269 otherwise, do nothing
1270 */
1271 static void group_retransmit_timer_on(struct igmp_group *group)
1272 {
1273 struct igmp_sock *igmp;
1274 struct pim_interface *pim_ifp;
1275 long lmqi_msec; /* Last Member Query Interval */
1276
1277 /* if group retransmit timer is running, do nothing */
1278 if (group->t_group_query_retransmit_timer) {
1279 return;
1280 }
1281
1282 igmp = group->group_igmp_sock;
1283 pim_ifp = igmp->interface->info;
1284
1285 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1286
1287 if (PIM_DEBUG_IGMP_TRACE) {
1288 char group_str[INET_ADDRSTRLEN];
1289 pim_inet4_dump("<group?>", group->group_addr, group_str,
1290 sizeof(group_str));
1291 zlog_debug(
1292 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1293 lmqi_msec / 1000, lmqi_msec % 1000, group_str,
1294 igmp->interface->name);
1295 }
1296
1297 thread_add_timer_msec(router->master, igmp_group_retransmit, group,
1298 lmqi_msec,
1299 &group->t_group_query_retransmit_timer);
1300 }
1301
1302 static long igmp_group_timer_remain_msec(struct igmp_group *group)
1303 {
1304 return pim_time_timer_remain_msec(group->t_group_timer);
1305 }
1306
1307 static long igmp_source_timer_remain_msec(struct igmp_source *source)
1308 {
1309 return pim_time_timer_remain_msec(source->t_source_timer);
1310 }
1311
1312 /*
1313 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1314 */
1315 static void group_query_send(struct igmp_group *group)
1316 {
1317 long lmqc; /* Last Member Query Count */
1318
1319 lmqc = group->group_igmp_sock->querier_robustness_variable;
1320
1321 /* lower group timer to lmqt */
1322 igmp_group_timer_lower_to_lmqt(group);
1323
1324 /* reset retransmission counter */
1325 group->group_specific_query_retransmit_count = lmqc;
1326
1327 /* immediately send group specific query (decrease retransmit counter by
1328 * 1)*/
1329 group_retransmit_group(group);
1330
1331 /* make sure group retransmit timer is running */
1332 group_retransmit_timer_on(group);
1333 }
1334
1335 /*
1336 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1337 */
1338 static void source_query_send_by_flag(struct igmp_group *group,
1339 int num_sources_tosend)
1340 {
1341 struct igmp_sock *igmp;
1342 struct pim_interface *pim_ifp;
1343 struct listnode *src_node;
1344 struct igmp_source *src;
1345 long lmqc; /* Last Member Query Count */
1346 long lmqi_msec; /* Last Member Query Interval */
1347 long lmqt_msec; /* Last Member Query Time */
1348
1349 zassert(num_sources_tosend > 0);
1350
1351 igmp = group->group_igmp_sock;
1352 pim_ifp = igmp->interface->info;
1353
1354 lmqc = igmp->querier_robustness_variable;
1355 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1356 lmqt_msec = lmqc * lmqi_msec;
1357
1358 /*
1359 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1360 Queries
1361
1362 (...) for each of the sources in X of group G, with source timer
1363 larger
1364 than LMQT:
1365 o Set number of retransmissions for each source to [Last Member
1366 Query Count].
1367 o Lower source timer to LMQT.
1368 */
1369 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1370 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1371 /* source "src" in X of group G */
1372 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1373 src->source_query_retransmit_count = lmqc;
1374 igmp_source_timer_lower_to_lmqt(src);
1375 }
1376 }
1377 }
1378
1379 /* send group-and-source specific queries */
1380 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1381
1382 /* make sure group retransmit timer is running */
1383 group_retransmit_timer_on(group);
1384 }
1385
1386 static void block_excl(struct igmp_group *group, int num_sources,
1387 struct in_addr *sources)
1388 {
1389 int num_sources_tosend = 0;
1390 int i;
1391
1392 /* 1. clear off SEND flag from all known sources (X,Y) */
1393 source_clear_send_flag(group->group_source_list);
1394
1395 /* 2. scan received sources (A) */
1396 for (i = 0; i < num_sources; ++i) {
1397 struct igmp_source *source;
1398 struct in_addr *src_addr;
1399
1400 src_addr = sources + i;
1401
1402 /* lookup reported source (A) in known sources (X,Y) */
1403 source = igmp_find_source_by_addr(group, *src_addr);
1404 if (!source) {
1405 /* 3: if not found, create source with Group Timer:
1406 * (A-X-Y)=Group Timer */
1407 long group_timer_msec;
1408 source = source_new(group, *src_addr);
1409
1410 zassert(!source->t_source_timer); /* timer == 0 */
1411 group_timer_msec = igmp_group_timer_remain_msec(group);
1412 igmp_source_timer_on(group, source, group_timer_msec);
1413 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1414 }
1415
1416 if (source->t_source_timer) {
1417 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1418 IGMP_SOURCE_DO_SEND(source->source_flags);
1419 ++num_sources_tosend;
1420 }
1421 }
1422
1423 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1424 if (num_sources_tosend > 0) {
1425 source_query_send_by_flag(group, num_sources_tosend);
1426 }
1427 }
1428
1429 static void block_incl(struct igmp_group *group, int num_sources,
1430 struct in_addr *sources)
1431 {
1432 int num_sources_tosend = 0;
1433 int i;
1434
1435 /* 1. clear off SEND flag from all known sources (B) */
1436 source_clear_send_flag(group->group_source_list);
1437
1438 /* 2. scan received sources (A) */
1439 for (i = 0; i < num_sources; ++i) {
1440 struct igmp_source *source;
1441 struct in_addr *src_addr;
1442
1443 src_addr = sources + i;
1444
1445 /* lookup reported source (A) in known sources (B) */
1446 source = igmp_find_source_by_addr(group, *src_addr);
1447 if (source) {
1448 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1449 IGMP_SOURCE_DO_SEND(source->source_flags);
1450 ++num_sources_tosend;
1451 }
1452 }
1453
1454 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1455 if (num_sources_tosend > 0) {
1456 source_query_send_by_flag(group, num_sources_tosend);
1457 }
1458 }
1459
1460 void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1461 struct in_addr group_addr, int num_sources,
1462 struct in_addr *sources)
1463 {
1464 struct interface *ifp = igmp->interface;
1465 struct igmp_group *group;
1466
1467 on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources,
1468 sources);
1469
1470 /* non-existant group is created as INCLUDE {empty} */
1471 group = igmp_add_group_by_addr(igmp, group_addr);
1472 if (!group) {
1473 return;
1474 }
1475
1476 if (group->group_filtermode_isexcl) {
1477 /* EXCLUDE mode */
1478 block_excl(group, num_sources, sources);
1479 } else {
1480 /* INCLUDE mode */
1481 block_incl(group, num_sources, sources);
1482 }
1483 }
1484
1485 void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1486 {
1487 struct igmp_sock *igmp;
1488 struct interface *ifp;
1489 struct pim_interface *pim_ifp;
1490 char *ifname;
1491 int lmqi_dsec; /* Last Member Query Interval */
1492 int lmqc; /* Last Member Query Count */
1493 int lmqt_msec; /* Last Member Query Time */
1494
1495 /*
1496 RFC 3376: 6.2.2. Definition of Group Timers
1497
1498 The group timer is only used when a group is in EXCLUDE mode and
1499 it represents the time for the *filter-mode* of the group to
1500 expire and switch to INCLUDE mode.
1501 */
1502 if (!group->group_filtermode_isexcl) {
1503 return;
1504 }
1505
1506 igmp = group->group_igmp_sock;
1507 ifp = igmp->interface;
1508 pim_ifp = ifp->info;
1509 ifname = ifp->name;
1510
1511 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1512 lmqc = igmp->querier_robustness_variable;
1513 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1514 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1515
1516 if (PIM_DEBUG_IGMP_TRACE) {
1517 char group_str[INET_ADDRSTRLEN];
1518 pim_inet4_dump("<group?>", group->group_addr, group_str,
1519 sizeof(group_str));
1520 zlog_debug(
1521 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1522 __PRETTY_FUNCTION__, group_str, ifname, lmqc, lmqi_dsec,
1523 lmqt_msec);
1524 }
1525
1526 zassert(group->group_filtermode_isexcl);
1527
1528 igmp_group_timer_on(group, lmqt_msec, ifname);
1529 }
1530
1531 void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1532 {
1533 struct igmp_group *group;
1534 struct igmp_sock *igmp;
1535 struct interface *ifp;
1536 struct pim_interface *pim_ifp;
1537 char *ifname;
1538 int lmqi_dsec; /* Last Member Query Interval */
1539 int lmqc; /* Last Member Query Count */
1540 int lmqt_msec; /* Last Member Query Time */
1541
1542 group = source->source_group;
1543 igmp = group->group_igmp_sock;
1544 ifp = igmp->interface;
1545 pim_ifp = ifp->info;
1546 ifname = ifp->name;
1547
1548 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1549 lmqc = igmp->querier_robustness_variable;
1550 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1551 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1552
1553 if (PIM_DEBUG_IGMP_TRACE) {
1554 char group_str[INET_ADDRSTRLEN];
1555 char source_str[INET_ADDRSTRLEN];
1556 pim_inet4_dump("<group?>", group->group_addr, group_str,
1557 sizeof(group_str));
1558 pim_inet4_dump("<source?>", source->source_addr, source_str,
1559 sizeof(source_str));
1560 zlog_debug(
1561 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1562 __PRETTY_FUNCTION__, group_str, source_str, ifname,
1563 lmqc, lmqi_dsec, lmqt_msec);
1564 }
1565
1566 igmp_source_timer_on(group, source, lmqt_msec);
1567 }
1568
1569 void igmp_v3_send_query(struct igmp_group *group, int fd, const char *ifname,
1570 char *query_buf, int query_buf_size, int num_sources,
1571 struct in_addr dst_addr, struct in_addr group_addr,
1572 int query_max_response_time_dsec, uint8_t s_flag,
1573 uint8_t querier_robustness_variable,
1574 uint16_t querier_query_interval)
1575 {
1576 ssize_t msg_size;
1577 uint8_t max_resp_code;
1578 uint8_t qqic;
1579 ssize_t sent;
1580 struct sockaddr_in to;
1581 socklen_t tolen;
1582 uint16_t checksum;
1583
1584 zassert(num_sources >= 0);
1585
1586 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1587 if (msg_size > query_buf_size) {
1588 flog_err(
1589 EC_LIB_DEVELOPMENT,
1590 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1591 __FILE__, __PRETTY_FUNCTION__, msg_size,
1592 query_buf_size);
1593 return;
1594 }
1595
1596 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1597 zassert((s_flag == 0) || (s_flag == 1));
1598
1599 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1600 qqic = igmp_msg_encode16to8(querier_query_interval);
1601
1602 /*
1603 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1604
1605 If non-zero, the QRV field contains the [Robustness Variable]
1606 value used by the querier, i.e., the sender of the Query. If the
1607 querier's [Robustness Variable] exceeds 7, the maximum value of
1608 the QRV field, the QRV is set to zero.
1609 */
1610 if (querier_robustness_variable > 7) {
1611 querier_robustness_variable = 0;
1612 }
1613
1614 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1615 query_buf[1] = max_resp_code;
1616 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
1617 0; /* for computing checksum */
1618 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
1619
1620 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1621 query_buf[9] = qqic;
1622 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) =
1623 htons(num_sources);
1624
1625 checksum = in_cksum(query_buf, msg_size);
1626 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1627
1628 if (PIM_DEBUG_IGMP_PACKETS) {
1629 char dst_str[INET_ADDRSTRLEN];
1630 char group_str[INET_ADDRSTRLEN];
1631 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1632 pim_inet4_dump("<group?>", group_addr, group_str,
1633 sizeof(group_str));
1634 zlog_debug(
1635 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1636 dst_str, ifname, group_str, num_sources, msg_size,
1637 s_flag, querier_robustness_variable,
1638 querier_query_interval, qqic);
1639 }
1640
1641 memset(&to, 0, sizeof(to));
1642 to.sin_family = AF_INET;
1643 to.sin_addr = dst_addr;
1644 tolen = sizeof(to);
1645
1646 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1647 (struct sockaddr *)&to, tolen);
1648 if (sent != (ssize_t)msg_size) {
1649 char dst_str[INET_ADDRSTRLEN];
1650 char group_str[INET_ADDRSTRLEN];
1651 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1652 pim_inet4_dump("<group?>", group_addr, group_str,
1653 sizeof(group_str));
1654 if (sent < 0) {
1655 zlog_warn(
1656 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1657 dst_str, ifname, group_str, msg_size, errno,
1658 safe_strerror(errno));
1659 } else {
1660 zlog_warn(
1661 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1662 dst_str, ifname, group_str, msg_size, sent);
1663 }
1664 return;
1665 }
1666
1667 /*
1668 s_flag sanity test: s_flag must be set for general queries
1669
1670 RFC 3376: 6.6.1. Timer Updates
1671
1672 When a router sends or receives a query with a clear Suppress
1673 Router-Side Processing flag, it must update its timers to reflect
1674 the correct timeout values for the group or sources being queried.
1675
1676 General queries don't trigger timer update.
1677 */
1678 if (!s_flag) {
1679 /* general query? */
1680 if (PIM_INADDR_IS_ANY(group_addr)) {
1681 char dst_str[INET_ADDRSTRLEN];
1682 char group_str[INET_ADDRSTRLEN];
1683 pim_inet4_dump("<dst?>", dst_addr, dst_str,
1684 sizeof(dst_str));
1685 pim_inet4_dump("<group?>", group_addr, group_str,
1686 sizeof(group_str));
1687 zlog_warn(
1688 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1689 __PRETTY_FUNCTION__, dst_str, ifname, group_str,
1690 num_sources);
1691 }
1692 }
1693 }
1694
1695 void igmp_v3_recv_query(struct igmp_sock *igmp, const char *from_str,
1696 char *igmp_msg)
1697 {
1698 struct interface *ifp;
1699 struct pim_interface *pim_ifp;
1700 struct in_addr group_addr;
1701 uint8_t resv_s_qrv = 0;
1702 uint8_t s_flag = 0;
1703 uint8_t qrv = 0;
1704 int i;
1705
1706 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1707 ifp = igmp->interface;
1708 pim_ifp = ifp->info;
1709
1710 /*
1711 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1712 *
1713 * Routers adopt the QRV value from the most recently received Query
1714 * as their own [Robustness Variable] value, unless that most
1715 * recently received QRV was zero, in which case the receivers use
1716 * the default [Robustness Variable] value specified in section 8.1
1717 * or a statically configured value.
1718 */
1719 resv_s_qrv = igmp_msg[8];
1720 qrv = 7 & resv_s_qrv;
1721 igmp->querier_robustness_variable =
1722 qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
1723
1724 /*
1725 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1726 *
1727 * Multicast routers that are not the current querier adopt the QQI
1728 * value from the most recently received Query as their own [Query
1729 * Interval] value, unless that most recently received QQI was zero,
1730 * in which case the receiving routers use the default.
1731 */
1732 if (igmp->t_other_querier_timer) {
1733 /* other querier present */
1734 uint8_t qqic;
1735 uint16_t qqi;
1736 qqic = igmp_msg[9];
1737 qqi = igmp_msg_decode8to16(qqic);
1738 igmp->querier_query_interval =
1739 qqi ? qqi : pim_ifp->igmp_default_query_interval;
1740
1741 if (PIM_DEBUG_IGMP_TRACE) {
1742 char ifaddr_str[INET_ADDRSTRLEN];
1743 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
1744 sizeof(ifaddr_str));
1745 zlog_debug(
1746 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1747 ifaddr_str,
1748 qqi ? "recv-non-default" : "default",
1749 igmp->querier_query_interval, qqic, from_str);
1750 }
1751 }
1752
1753 /*
1754 * RFC 3376: 6.6.1. Timer Updates
1755 *
1756 * When a router sends or receives a query with a clear Suppress
1757 * Router-Side Processing flag, it must update its timers to reflect
1758 * the correct timeout values for the group or sources being queried.
1759 *
1760 * General queries don't trigger timer update.
1761 */
1762 s_flag = (1 << 3) & resv_s_qrv;
1763
1764 if (!s_flag) {
1765 /* s_flag is clear */
1766
1767 if (PIM_INADDR_IS_ANY(group_addr)) {
1768 /* this is a general query */
1769 /* log that general query should have the s_flag set */
1770 zlog_warn(
1771 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1772 from_str, ifp->name);
1773 } else {
1774 struct igmp_group *group;
1775
1776 /* this is a non-general query: perform timer updates */
1777
1778 group = find_group_by_addr(igmp, group_addr);
1779 if (group) {
1780 int recv_num_sources = ntohs(*(
1781 uint16_t
1782 *)(igmp_msg
1783 + IGMP_V3_NUMSOURCES_OFFSET));
1784
1785 /*
1786 * RFC 3376: 6.6.1. Timer Updates
1787 * Query Q(G,A): Source Timer for sources in A
1788 * are lowered to LMQT
1789 * Query Q(G): Group Timer is lowered to LMQT
1790 */
1791 if (recv_num_sources < 1) {
1792 /* Query Q(G): Group Timer is lowered to
1793 * LMQT */
1794
1795 igmp_group_timer_lower_to_lmqt(group);
1796 } else {
1797 /* Query Q(G,A): Source Timer for
1798 * sources in A are lowered to LMQT */
1799
1800 /* Scan sources in query and lower their
1801 * timers to LMQT */
1802 struct in_addr *sources =
1803 (struct in_addr
1804 *)(igmp_msg
1805 + IGMP_V3_SOURCES_OFFSET);
1806 for (i = 0; i < recv_num_sources; ++i) {
1807 struct in_addr src_addr;
1808 struct igmp_source *src;
1809 memcpy(&src_addr, sources + i,
1810 sizeof(struct in_addr));
1811 src = igmp_find_source_by_addr(
1812 group, src_addr);
1813 if (src) {
1814 igmp_source_timer_lower_to_lmqt(
1815 src);
1816 }
1817 }
1818 }
1819 } else {
1820 char group_str[INET_ADDRSTRLEN];
1821 pim_inet4_dump("<group?>", group_addr,
1822 group_str, sizeof(group_str));
1823 zlog_warn(
1824 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1825 from_str, ifp->name, group_str);
1826 }
1827 }
1828 } /* s_flag is clear: timer updates */
1829 }
1830
1831 int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from,
1832 const char *from_str, char *igmp_msg, int igmp_msg_len)
1833 {
1834 uint16_t recv_checksum;
1835 uint16_t checksum;
1836 int num_groups;
1837 uint8_t *group_record;
1838 uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
1839 struct interface *ifp = igmp->interface;
1840 int i;
1841 int local_ncb = 0;
1842 struct pim_interface *pim_ifp;
1843
1844 if (igmp->mtrace_only)
1845 return 0;
1846
1847 pim_ifp = igmp->interface->info;
1848
1849 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1850 zlog_warn(
1851 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1852 from_str, ifp->name, igmp_msg_len,
1853 IGMP_V3_MSG_MIN_SIZE);
1854 return -1;
1855 }
1856
1857 recv_checksum = *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET);
1858
1859 /* for computing checksum */
1860 *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
1861
1862 checksum = in_cksum(igmp_msg, igmp_msg_len);
1863 if (checksum != recv_checksum) {
1864 zlog_warn(
1865 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1866 from_str, ifp->name, recv_checksum, checksum);
1867 return -1;
1868 }
1869
1870 /* Collecting IGMP Rx stats */
1871 igmp->rx_stats.report_v3++;
1872
1873 num_groups = ntohs(
1874 *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1875 if (num_groups < 1) {
1876 zlog_warn(
1877 "Recv IGMP report v3 from %s on %s: missing group records",
1878 from_str, ifp->name);
1879 return -1;
1880 }
1881
1882 if (PIM_DEBUG_IGMP_PACKETS) {
1883 zlog_debug(
1884 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1885 from_str, ifp->name, igmp_msg_len, checksum,
1886 num_groups);
1887 }
1888
1889 group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1890
1891 /* Scan groups */
1892 for (i = 0; i < num_groups; ++i) {
1893 struct in_addr rec_group;
1894 uint8_t *sources;
1895 uint8_t *src;
1896 int rec_type;
1897 int rec_auxdatalen;
1898 int rec_num_sources;
1899 int j;
1900 struct prefix lncb;
1901 struct prefix g;
1902 bool filtered = false;
1903
1904 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
1905 > report_pastend) {
1906 zlog_warn(
1907 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1908 from_str, ifp->name);
1909 return -1;
1910 }
1911
1912 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1913 rec_auxdatalen =
1914 group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1915 rec_num_sources = ntohs(*(
1916 uint16_t *)(group_record
1917 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1918
1919 memcpy(&rec_group,
1920 group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET,
1921 sizeof(struct in_addr));
1922
1923 if (PIM_DEBUG_IGMP_PACKETS) {
1924 zlog_debug(
1925 "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1926 from_str, ifp->name, i, rec_type,
1927 rec_auxdatalen, rec_num_sources,
1928 inet_ntoa(rec_group));
1929 }
1930
1931 /* Scan sources */
1932
1933 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1934
1935 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1936
1937 if ((src + 4) > report_pastend) {
1938 zlog_warn(
1939 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1940 from_str, ifp->name);
1941 return -1;
1942 }
1943
1944 if (PIM_DEBUG_IGMP_PACKETS) {
1945 char src_str[200];
1946
1947 if (!inet_ntop(AF_INET, src, src_str,
1948 sizeof(src_str)))
1949 sprintf(src_str, "<source?>");
1950
1951 zlog_debug(
1952 "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1953 from_str, ifp->name, i,
1954 inet_ntoa(rec_group), src_str);
1955 }
1956 } /* for (sources) */
1957
1958
1959 lncb.family = AF_INET;
1960 lncb.u.prefix4.s_addr = 0x000000E0;
1961 lncb.prefixlen = 24;
1962
1963 g.family = AF_INET;
1964 g.u.prefix4 = rec_group;
1965 g.prefixlen = 32;
1966
1967 /* determine filtering status for group */
1968 filtered = pim_is_group_filtered(ifp->info, &rec_group);
1969
1970 if (PIM_DEBUG_IGMP_PACKETS && filtered)
1971 zlog_debug(
1972 "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
1973 inet_ntoa(rec_group), from_str, ifp->name,
1974 pim_ifp->boundary_oil_plist);
1975
1976 /*
1977 * If we receive a igmp report with the group in 224.0.0.0/24
1978 * then we should ignore it
1979 */
1980 if (prefix_match(&lncb, &g))
1981 local_ncb = 1;
1982
1983 if (!local_ncb && !filtered)
1984 switch (rec_type) {
1985 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1986 igmpv3_report_isin(igmp, from, rec_group,
1987 rec_num_sources,
1988 (struct in_addr *)sources);
1989 break;
1990 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1991 igmpv3_report_isex(
1992 igmp, from, rec_group, rec_num_sources,
1993 (struct in_addr *)sources, 0);
1994 break;
1995 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
1996 igmpv3_report_toin(igmp, from, rec_group,
1997 rec_num_sources,
1998 (struct in_addr *)sources);
1999 break;
2000 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
2001 igmpv3_report_toex(igmp, from, rec_group,
2002 rec_num_sources,
2003 (struct in_addr *)sources);
2004 break;
2005 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
2006 igmpv3_report_allow(igmp, from, rec_group,
2007 rec_num_sources,
2008 (struct in_addr *)sources);
2009 break;
2010 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
2011 igmpv3_report_block(igmp, from, rec_group,
2012 rec_num_sources,
2013 (struct in_addr *)sources);
2014 break;
2015 default:
2016 zlog_warn(
2017 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2018 from_str, ifp->name, rec_type);
2019 }
2020
2021 group_record +=
2022 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
2023 local_ncb = 0;
2024
2025 } /* for (group records) */
2026
2027 return 0;
2028 }