]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv3.c
Merge pull request #254 from donaldsharp/shutdown3
[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_TIMER_MSEC_ON(master, source->t_source_timer,
219 igmp_source_timer,
220 source, interval_msec);
221 zassert(source->t_source_timer);
222
223 /*
224 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
225
226 Source timer switched from (T == 0) to (T > 0): enable forwarding.
227 */
228 igmp_source_forward_start(source);
229 }
230
231 void igmp_source_reset_gmi(struct igmp_sock *igmp,
232 struct igmp_group *group,
233 struct igmp_source *source)
234 {
235 long group_membership_interval_msec;
236 struct pim_interface *pim_ifp;
237 struct interface *ifp;
238
239 ifp = igmp->interface;
240 pim_ifp = ifp->info;
241
242 group_membership_interval_msec =
243 PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
244 igmp->querier_query_interval,
245 pim_ifp->igmp_query_max_response_time_dsec);
246
247 if (PIM_DEBUG_IGMP_TRACE) {
248 char group_str[INET_ADDRSTRLEN];
249 char source_str[INET_ADDRSTRLEN];
250
251 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
252 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
253
254 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
255 source_str,
256 group_membership_interval_msec / 1000,
257 group_membership_interval_msec % 1000,
258 group_str,
259 ifp->name);
260 }
261
262 igmp_source_timer_on(group, source,
263 group_membership_interval_msec);
264 }
265
266 static void source_mark_delete_flag(struct igmp_group *group)
267 {
268 struct listnode *src_node;
269 struct igmp_source *src;
270
271 for (ALL_LIST_ELEMENTS_RO (group->group_source_list, src_node, src)) {
272 IGMP_SOURCE_DO_DELETE(src->source_flags);
273 }
274 }
275
276 static void source_mark_send_flag (struct igmp_group *group)
277 {
278 struct listnode *src_node;
279 struct igmp_source *src;
280
281 for (ALL_LIST_ELEMENTS_RO (group->group_source_list, src_node, src)) {
282 IGMP_SOURCE_DO_SEND(src->source_flags);
283 }
284 }
285
286 static int source_mark_send_flag_by_timer (struct igmp_group *group)
287 {
288 struct listnode *src_node;
289 struct igmp_source *src;
290 int num_marked_sources = 0;
291
292 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
293 /* Is source timer running? */
294 if (src->t_source_timer) {
295 IGMP_SOURCE_DO_SEND(src->source_flags);
296 ++num_marked_sources;
297 }
298 else {
299 IGMP_SOURCE_DONT_SEND(src->source_flags);
300 }
301 }
302
303 return num_marked_sources;
304 }
305
306 static void source_clear_send_flag(struct list *source_list)
307 {
308 struct listnode *src_node;
309 struct igmp_source *src;
310
311 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
312 IGMP_SOURCE_DONT_SEND(src->source_flags);
313 }
314 }
315
316 /*
317 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
318 */
319 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
320 {
321 zassert(group->group_filtermode_isexcl);
322
323 if (listcount(group->group_source_list) < 1) {
324 igmp_anysource_forward_start(group);
325 }
326 }
327
328 void igmp_source_free(struct igmp_source *source)
329 {
330 /* make sure there is no source timer running */
331 zassert(!source->t_source_timer);
332
333 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
334 }
335
336 static void source_channel_oil_detach(struct igmp_source *source)
337 {
338 if (source->source_channel_oil) {
339 pim_channel_oil_del(source->source_channel_oil);
340 source->source_channel_oil = NULL;
341 }
342 }
343
344 /*
345 igmp_source_delete: stop fowarding, and delete the source
346 igmp_source_forward_stop: stop fowarding, but keep the source
347 */
348 void igmp_source_delete(struct igmp_source *source)
349 {
350 struct igmp_group *group;
351 struct in_addr src;
352
353 group = source->source_group;
354
355 if (PIM_DEBUG_IGMP_TRACE) {
356 char group_str[INET_ADDRSTRLEN];
357 char source_str[INET_ADDRSTRLEN];
358 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
359 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
360 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
361 source_str, group_str,
362 group->group_igmp_sock->fd,
363 group->group_igmp_sock->interface->name);
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_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1331 igmp_group_retransmit,
1332 group, lmqi_msec);
1333 }
1334
1335 static long igmp_group_timer_remain_msec(struct igmp_group *group)
1336 {
1337 return pim_time_timer_remain_msec(group->t_group_timer);
1338 }
1339
1340 static long igmp_source_timer_remain_msec(struct igmp_source *source)
1341 {
1342 return pim_time_timer_remain_msec(source->t_source_timer);
1343 }
1344
1345 /*
1346 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1347 */
1348 static void group_query_send(struct igmp_group *group)
1349 {
1350 long lmqc; /* Last Member Query Count */
1351
1352 lmqc = group->group_igmp_sock->querier_robustness_variable;
1353
1354 /* lower group timer to lmqt */
1355 igmp_group_timer_lower_to_lmqt(group);
1356
1357 /* reset retransmission counter */
1358 group->group_specific_query_retransmit_count = lmqc;
1359
1360 /* immediately send group specific query (decrease retransmit counter by 1)*/
1361 group_retransmit_group(group);
1362
1363 /* make sure group retransmit timer is running */
1364 group_retransmit_timer_on(group);
1365 }
1366
1367 /*
1368 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1369 */
1370 static void source_query_send_by_flag(struct igmp_group *group,
1371 int num_sources_tosend)
1372 {
1373 struct igmp_sock *igmp;
1374 struct pim_interface *pim_ifp;
1375 struct listnode *src_node;
1376 struct igmp_source *src;
1377 long lmqc; /* Last Member Query Count */
1378 long lmqi_msec; /* Last Member Query Interval */
1379 long lmqt_msec; /* Last Member Query Time */
1380
1381 zassert(num_sources_tosend > 0);
1382
1383 igmp = group->group_igmp_sock;
1384 pim_ifp = igmp->interface->info;
1385
1386 lmqc = igmp->querier_robustness_variable;
1387 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1388 lmqt_msec = lmqc * lmqi_msec;
1389
1390 /*
1391 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1392
1393 (...) for each of the sources in X of group G, with source timer larger
1394 than LMQT:
1395 o Set number of retransmissions for each source to [Last Member
1396 Query Count].
1397 o Lower source timer to LMQT.
1398 */
1399 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1400 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1401 /* source "src" in X of group G */
1402 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1403 src->source_query_retransmit_count = lmqc;
1404 igmp_source_timer_lower_to_lmqt(src);
1405 }
1406 }
1407 }
1408
1409 /* send group-and-source specific queries */
1410 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1411
1412 /* make sure group retransmit timer is running */
1413 group_retransmit_timer_on(group);
1414 }
1415
1416 static void block_excl(struct igmp_group *group,
1417 int num_sources, struct in_addr *sources)
1418 {
1419 int num_sources_tosend = 0;
1420 int i;
1421
1422 /* 1. clear off SEND flag from all known sources (X,Y) */
1423 source_clear_send_flag(group->group_source_list);
1424
1425 /* 2. scan received sources (A) */
1426 for (i = 0; i < num_sources; ++i) {
1427 struct igmp_source *source;
1428 struct in_addr *src_addr;
1429
1430 src_addr = sources + i;
1431
1432 /* lookup reported source (A) in known sources (X,Y) */
1433 source = igmp_find_source_by_addr(group, *src_addr);
1434 if (!source) {
1435 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1436 long group_timer_msec;
1437 source = source_new(group, *src_addr);
1438 if (!source) {
1439 /* ugh, internal malloc failure, skip source */
1440 continue;
1441 }
1442
1443 zassert(!source->t_source_timer); /* timer == 0 */
1444 group_timer_msec = igmp_group_timer_remain_msec(group);
1445 igmp_source_timer_on(group, source, group_timer_msec);
1446 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1447 }
1448
1449 if (source->t_source_timer) {
1450 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1451 IGMP_SOURCE_DO_SEND(source->source_flags);
1452 ++num_sources_tosend;
1453 }
1454 }
1455
1456 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1457 if (num_sources_tosend > 0) {
1458 source_query_send_by_flag(group, num_sources_tosend);
1459 }
1460 }
1461
1462 static void block_incl(struct igmp_group *group,
1463 int num_sources, struct in_addr *sources)
1464 {
1465 int num_sources_tosend = 0;
1466 int i;
1467
1468 /* 1. clear off SEND flag from all known sources (B) */
1469 source_clear_send_flag(group->group_source_list);
1470
1471 /* 2. scan received sources (A) */
1472 for (i = 0; i < num_sources; ++i) {
1473 struct igmp_source *source;
1474 struct in_addr *src_addr;
1475
1476 src_addr = sources + i;
1477
1478 /* lookup reported source (A) in known sources (B) */
1479 source = igmp_find_source_by_addr(group, *src_addr);
1480 if (source) {
1481 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1482 IGMP_SOURCE_DO_SEND(source->source_flags);
1483 ++num_sources_tosend;
1484 }
1485 }
1486
1487 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1488 if (num_sources_tosend > 0) {
1489 source_query_send_by_flag(group, num_sources_tosend);
1490 }
1491 }
1492
1493 void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1494 struct in_addr group_addr,
1495 int num_sources, struct in_addr *sources)
1496 {
1497 struct interface *ifp = igmp->interface;
1498 struct igmp_group *group;
1499
1500 on_trace(__PRETTY_FUNCTION__,
1501 ifp, from, group_addr, num_sources, sources);
1502
1503 /* non-existant group is created as INCLUDE {empty} */
1504 group = igmp_add_group_by_addr(igmp, group_addr);
1505 if (!group) {
1506 return;
1507 }
1508
1509 if (group->group_filtermode_isexcl) {
1510 /* EXCLUDE mode */
1511 block_excl(group, num_sources, sources);
1512 }
1513 else {
1514 /* INCLUDE mode */
1515 block_incl(group, num_sources, sources);
1516 }
1517 }
1518
1519 void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1520 {
1521 struct igmp_sock *igmp;
1522 struct interface *ifp;
1523 struct pim_interface *pim_ifp;
1524 char *ifname;
1525 int lmqi_dsec; /* Last Member Query Interval */
1526 int lmqc; /* Last Member Query Count */
1527 int lmqt_msec; /* Last Member Query Time */
1528
1529 /*
1530 RFC 3376: 6.2.2. Definition of Group Timers
1531
1532 The group timer is only used when a group is in EXCLUDE mode and
1533 it represents the time for the *filter-mode* of the group to
1534 expire and switch to INCLUDE mode.
1535 */
1536 if (!group->group_filtermode_isexcl) {
1537 return;
1538 }
1539
1540 igmp = group->group_igmp_sock;
1541 ifp = igmp->interface;
1542 pim_ifp = ifp->info;
1543 ifname = ifp->name;
1544
1545 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1546 lmqc = igmp->querier_robustness_variable;
1547 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1548
1549 if (PIM_DEBUG_IGMP_TRACE) {
1550 char group_str[INET_ADDRSTRLEN];
1551 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1552 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1553 __PRETTY_FUNCTION__,
1554 group_str, ifname,
1555 lmqc, lmqi_dsec, lmqt_msec);
1556 }
1557
1558 zassert(group->group_filtermode_isexcl);
1559
1560 igmp_group_timer_on(group, lmqt_msec, ifname);
1561 }
1562
1563 void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1564 {
1565 struct igmp_group *group;
1566 struct igmp_sock *igmp;
1567 struct interface *ifp;
1568 struct pim_interface *pim_ifp;
1569 char *ifname;
1570 int lmqi_dsec; /* Last Member Query Interval */
1571 int lmqc; /* Last Member Query Count */
1572 int lmqt_msec; /* Last Member Query Time */
1573
1574 group = source->source_group;
1575 igmp = group->group_igmp_sock;
1576 ifp = igmp->interface;
1577 pim_ifp = ifp->info;
1578 ifname = ifp->name;
1579
1580 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1581 lmqc = igmp->querier_robustness_variable;
1582 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1583
1584 if (PIM_DEBUG_IGMP_TRACE) {
1585 char group_str[INET_ADDRSTRLEN];
1586 char source_str[INET_ADDRSTRLEN];
1587 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1588 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1589 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1590 __PRETTY_FUNCTION__,
1591 group_str, source_str, ifname,
1592 lmqc, lmqi_dsec, lmqt_msec);
1593 }
1594
1595 igmp_source_timer_on(group, source, lmqt_msec);
1596 }
1597
1598 void
1599 igmp_v3_send_query (struct igmp_group *group,
1600 int fd,
1601 const char *ifname,
1602 char *query_buf,
1603 int query_buf_size,
1604 int num_sources,
1605 struct in_addr dst_addr,
1606 struct in_addr group_addr,
1607 int query_max_response_time_dsec,
1608 uint8_t s_flag,
1609 uint8_t querier_robustness_variable,
1610 uint16_t querier_query_interval)
1611 {
1612 ssize_t msg_size;
1613 uint8_t max_resp_code;
1614 uint8_t qqic;
1615 ssize_t sent;
1616 struct sockaddr_in to;
1617 socklen_t tolen;
1618 uint16_t checksum;
1619
1620 zassert(num_sources >= 0);
1621
1622 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1623 if (msg_size > query_buf_size) {
1624 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1625 __FILE__, __PRETTY_FUNCTION__,
1626 msg_size, query_buf_size);
1627 return;
1628 }
1629
1630 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1631 zassert((s_flag == 0) || (s_flag == 1));
1632
1633 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1634 qqic = igmp_msg_encode16to8(querier_query_interval);
1635
1636 /*
1637 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1638
1639 If non-zero, the QRV field contains the [Robustness Variable]
1640 value used by the querier, i.e., the sender of the Query. If the
1641 querier's [Robustness Variable] exceeds 7, the maximum value of
1642 the QRV field, the QRV is set to zero.
1643 */
1644 if (querier_robustness_variable > 7) {
1645 querier_robustness_variable = 0;
1646 }
1647
1648 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1649 query_buf[1] = max_resp_code;
1650 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */
1651 memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
1652
1653 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1654 query_buf[9] = qqic;
1655 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1656
1657 checksum = in_cksum(query_buf, msg_size);
1658 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1659
1660 if (PIM_DEBUG_IGMP_PACKETS) {
1661 char dst_str[INET_ADDRSTRLEN];
1662 char group_str[INET_ADDRSTRLEN];
1663 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1664 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1665 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",
1666 dst_str, ifname, group_str,
1667 num_sources, msg_size, s_flag, querier_robustness_variable,
1668 querier_query_interval, qqic);
1669 }
1670
1671 memset(&to, 0, sizeof(to));
1672 to.sin_family = AF_INET;
1673 to.sin_addr = dst_addr;
1674 tolen = sizeof(to);
1675
1676 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1677 (struct sockaddr *)&to, tolen);
1678 if (sent != (ssize_t) msg_size) {
1679 char dst_str[INET_ADDRSTRLEN];
1680 char group_str[INET_ADDRSTRLEN];
1681 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1682 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1683 if (sent < 0) {
1684 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1685 dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
1686 }
1687 else {
1688 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1689 dst_str, ifname, group_str, msg_size, sent);
1690 }
1691 return;
1692 }
1693
1694 /*
1695 s_flag sanity test: s_flag must be set for general queries
1696
1697 RFC 3376: 6.6.1. Timer Updates
1698
1699 When a router sends or receives a query with a clear Suppress
1700 Router-Side Processing flag, it must update its timers to reflect
1701 the correct timeout values for the group or sources being queried.
1702
1703 General queries don't trigger timer update.
1704 */
1705 if (!s_flag) {
1706 /* general query? */
1707 if (PIM_INADDR_IS_ANY(group_addr)) {
1708 char dst_str[INET_ADDRSTRLEN];
1709 char group_str[INET_ADDRSTRLEN];
1710 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1711 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1712 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1713 __PRETTY_FUNCTION__,
1714 dst_str, ifname, group_str, num_sources);
1715 }
1716 }
1717 }
1718
1719 void
1720 igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, char *igmp_msg)
1721 {
1722 struct interface *ifp;
1723 struct pim_interface *pim_ifp;
1724 struct in_addr group_addr;
1725 uint8_t resv_s_qrv = 0;
1726 uint8_t s_flag = 0;
1727 uint8_t qrv = 0;
1728 int i;
1729
1730 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1731 ifp = igmp->interface;
1732 pim_ifp = ifp->info;
1733
1734 /*
1735 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1736 *
1737 * Routers adopt the QRV value from the most recently received Query
1738 * as their own [Robustness Variable] value, unless that most
1739 * recently received QRV was zero, in which case the receivers use
1740 * the default [Robustness Variable] value specified in section 8.1
1741 * or a statically configured value.
1742 */
1743 resv_s_qrv = igmp_msg[8];
1744 qrv = 7 & resv_s_qrv;
1745 igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
1746
1747 /*
1748 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1749 *
1750 * Multicast routers that are not the current querier adopt the QQI
1751 * value from the most recently received Query as their own [Query
1752 * Interval] value, unless that most recently received QQI was zero,
1753 * in which case the receiving routers use the default.
1754 */
1755 if (igmp->t_other_querier_timer) {
1756 /* other querier present */
1757 uint8_t qqic;
1758 uint16_t qqi;
1759 qqic = igmp_msg[9];
1760 qqi = igmp_msg_decode8to16(qqic);
1761 igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
1762
1763 if (PIM_DEBUG_IGMP_TRACE) {
1764 char ifaddr_str[INET_ADDRSTRLEN];
1765 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
1766 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1767 ifaddr_str,
1768 qqi ? "recv-non-default" : "default",
1769 igmp->querier_query_interval,
1770 qqic,
1771 from_str);
1772 }
1773 }
1774
1775 /*
1776 * RFC 3376: 6.6.1. Timer Updates
1777 *
1778 * When a router sends or receives a query with a clear Suppress
1779 * Router-Side Processing flag, it must update its timers to reflect
1780 * the correct timeout values for the group or sources being queried.
1781 *
1782 * General queries don't trigger timer update.
1783 */
1784 s_flag = (1 << 3) & resv_s_qrv;
1785
1786 if (!s_flag) {
1787 /* s_flag is clear */
1788
1789 if (PIM_INADDR_IS_ANY(group_addr)) {
1790 /* this is a general query */
1791 /* log that general query should have the s_flag set */
1792 zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1793 from_str, ifp->name);
1794 } else {
1795 struct igmp_group *group;
1796
1797 /* this is a non-general query: perform timer updates */
1798
1799 group = find_group_by_addr(igmp, group_addr);
1800 if (group) {
1801 int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
1802
1803 /*
1804 * RFC 3376: 6.6.1. Timer Updates
1805 * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
1806 * Query Q(G): Group Timer is lowered to LMQT
1807 */
1808 if (recv_num_sources < 1) {
1809 /* Query Q(G): Group Timer is lowered to LMQT */
1810
1811 igmp_group_timer_lower_to_lmqt(group);
1812 } else {
1813 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
1814
1815 /* Scan sources in query and lower their timers to LMQT */
1816 struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
1817 for (i = 0; i < recv_num_sources; ++i) {
1818 struct in_addr src_addr;
1819 struct igmp_source *src;
1820 memcpy(&src_addr, sources + i, sizeof(struct in_addr));
1821 src = igmp_find_source_by_addr(group, src_addr);
1822 if (src) {
1823 igmp_source_timer_lower_to_lmqt(src);
1824 }
1825 }
1826 }
1827 } else {
1828 char group_str[INET_ADDRSTRLEN];
1829 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1830 zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
1831 from_str, ifp->name, group_str);
1832 }
1833 }
1834 } /* s_flag is clear: timer updates */
1835 }
1836
1837 int
1838 igmp_v3_recv_report (struct igmp_sock *igmp,
1839 struct in_addr from, const char *from_str,
1840 char *igmp_msg, int igmp_msg_len)
1841 {
1842 uint16_t recv_checksum;
1843 uint16_t checksum;
1844 int num_groups;
1845 uint8_t *group_record;
1846 uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
1847 struct interface *ifp = igmp->interface;
1848 int i;
1849 int local_ncb = 0;
1850
1851 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1852 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1853 from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
1854 return -1;
1855 }
1856
1857 recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
1858
1859 /* for computing checksum */
1860 *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
1861
1862 checksum = in_cksum(igmp_msg, igmp_msg_len);
1863 if (checksum != recv_checksum) {
1864 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1865 from_str, ifp->name, recv_checksum, checksum);
1866 return -1;
1867 }
1868
1869 num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1870 if (num_groups < 1) {
1871 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
1872 from_str, ifp->name);
1873 return -1;
1874 }
1875
1876 if (PIM_DEBUG_IGMP_PACKETS) {
1877 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1878 from_str, ifp->name, igmp_msg_len, checksum, num_groups);
1879 }
1880
1881 group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1882
1883 /* Scan groups */
1884 for (i = 0; i < num_groups; ++i) {
1885 struct in_addr rec_group;
1886 uint8_t *sources;
1887 uint8_t *src;
1888 int rec_type;
1889 int rec_auxdatalen;
1890 int rec_num_sources;
1891 int j;
1892 struct prefix lncb;
1893 struct prefix g;
1894
1895 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
1896 zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
1897 from_str, ifp->name);
1898 return -1;
1899 }
1900
1901 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1902 rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1903 rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1904
1905 memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
1906
1907 if (PIM_DEBUG_IGMP_PACKETS) {
1908 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1909 from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
1910 }
1911
1912 /* Scan sources */
1913
1914 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1915
1916 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1917
1918 if ((src + 4) > report_pastend) {
1919 zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
1920 from_str, ifp->name);
1921 return -1;
1922 }
1923
1924 if (PIM_DEBUG_IGMP_PACKETS) {
1925 char src_str[200];
1926
1927 if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
1928 sprintf(src_str, "<source?>");
1929
1930 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1931 from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
1932 }
1933 } /* for (sources) */
1934
1935
1936 lncb.family = AF_INET;
1937 lncb.u.prefix4.s_addr = 0x000000E0;
1938 lncb.prefixlen = 24;
1939
1940 g.family = AF_INET;
1941 g.u.prefix4 = rec_group;
1942 g.prefixlen = 32;
1943 /*
1944 * If we receive a igmp report with the group in 224.0.0.0/24
1945 * then we should ignore it
1946 */
1947 if (prefix_match(&lncb, &g))
1948 local_ncb = 1;
1949
1950 if (!local_ncb)
1951 switch (rec_type) {
1952 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1953 igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
1954 break;
1955 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1956 igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources, 0);
1957 break;
1958 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
1959 igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
1960 break;
1961 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
1962 igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
1963 break;
1964 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
1965 igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
1966 break;
1967 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
1968 igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
1969 break;
1970 default:
1971 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
1972 from_str, ifp->name, rec_type);
1973 }
1974
1975 group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
1976 local_ncb = 0;
1977
1978 } /* for (group records) */
1979
1980 return 0;
1981 }