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