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