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