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