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