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