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