3 Copyright (C) 2008 Everton da Silva Marques
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.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
20 $QuaggaId: $Format:%an, %ai, %h$ $
29 #include "pim_iface.h"
31 #include "pim_igmpv3.h"
35 #include "pim_zebra.h"
38 static void group_retransmit_timer_on(struct igmp_group
*group
);
39 static long igmp_group_timer_remain_msec(struct igmp_group
*group
);
40 static long igmp_source_timer_remain_msec(struct igmp_source
*source
);
41 static void group_query_send(struct igmp_group
*group
);
42 static void source_query_send_by_flag(struct igmp_group
*group
,
43 int num_sources_tosend
);
45 static void on_trace(const char *label
,
46 struct interface
*ifp
, struct in_addr from
,
47 struct in_addr group_addr
,
48 int num_sources
, struct in_addr
*sources
)
50 if (PIM_DEBUG_IGMP_TRACE
) {
54 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
55 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
57 zlog_debug("%s: from %s on %s: group=%s sources=%d",
58 label
, from_str
, ifp
->name
, group_str
, num_sources
);
62 int igmp_group_compat_mode(const struct igmp_sock
*igmp
,
63 const struct igmp_group
*group
)
65 struct pim_interface
*pim_ifp
;
67 long older_host_present_interval_dsec
;
70 zassert(igmp
->interface
);
71 zassert(igmp
->interface
->info
);
73 pim_ifp
= igmp
->interface
->info
;
76 RFC 3376: 8.13. Older Host Present Interval
78 This value MUST be ((the Robustness Variable) times (the Query
79 Interval)) plus (one Query Response Interval).
81 older_host_present_interval_dsec = \
82 igmp->querier_robustness_variable * \
83 10 * igmp->querier_query_interval + \
84 pim_ifp->query_max_response_time_dsec;
86 older_host_present_interval_dsec
=
87 PIM_IGMP_OHPI_DSEC(igmp
->querier_robustness_variable
,
88 igmp
->querier_query_interval
,
89 pim_ifp
->igmp_query_max_response_time_dsec
);
91 now_dsec
= pim_time_monotonic_dsec();
93 /* broken timer logged by pim_time_monotonic_dsec() */
97 if ((now_dsec
- group
->last_igmp_v1_report_dsec
) < older_host_present_interval_dsec
)
98 return 1; /* IGMPv1 */
100 if ((now_dsec
- group
->last_igmp_v2_report_dsec
) < older_host_present_interval_dsec
)
101 return 2; /* IGMPv2 */
103 return 3; /* IGMPv3 */
106 void igmp_group_reset_gmi(struct igmp_group
*group
)
108 long group_membership_interval_msec
;
109 struct pim_interface
*pim_ifp
;
110 struct igmp_sock
*igmp
;
111 struct interface
*ifp
;
113 igmp
= group
->group_igmp_sock
;
114 ifp
= igmp
->interface
;
118 RFC 3376: 8.4. Group Membership Interval
120 The Group Membership Interval is the amount of time that must pass
121 before a multicast router decides there are no more members of a
122 group or a particular source on a network.
124 This value MUST be ((the Robustness Variable) times (the Query
125 Interval)) plus (one Query Response Interval).
127 group_membership_interval_msec = querier_robustness_variable *
128 (1000 * querier_query_interval) +
129 100 * query_response_interval_dsec;
131 group_membership_interval_msec
=
132 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
133 igmp
->querier_query_interval
,
134 pim_ifp
->igmp_query_max_response_time_dsec
);
136 if (PIM_DEBUG_IGMP_TRACE
) {
138 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
139 zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
141 group_membership_interval_msec
/ 1000,
142 group_membership_interval_msec
% 1000,
147 RFC 3376: 6.2.2. Definition of Group Timers
149 The group timer is only used when a group is in EXCLUDE mode and
150 it represents the time for the *filter-mode* of the group to
151 expire and switch to INCLUDE mode.
153 zassert(group
->group_filtermode_isexcl
);
155 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
158 static int igmp_source_timer(struct thread
*t
)
160 struct igmp_source
*source
;
161 struct igmp_group
*group
;
164 source
= THREAD_ARG(t
);
167 group
= source
->source_group
;
169 if (PIM_DEBUG_IGMP_TRACE
) {
171 char source_str
[100];
172 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
173 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
174 zlog_debug("%s: Source timer expired for group %s source %s on %s",
176 group_str
, source_str
,
177 group
->group_igmp_sock
->interface
->name
);
180 zassert(source
->t_source_timer
);
181 source
->t_source_timer
= 0;
184 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
187 Filter-Mode Source Timer Value Action
188 ----------- ------------------ ------
189 INCLUDE TIMER == 0 Suggest to stop forwarding
190 traffic from source and
191 remove source record. If
192 there are no more source
193 records for the group, delete
196 EXCLUDE TIMER == 0 Suggest to not forward
198 (DO NOT remove record)
200 Source timer switched from (T > 0) to (T == 0): disable forwarding.
203 zassert(!source
->t_source_timer
);
205 if (group
->group_filtermode_isexcl
) {
208 igmp_source_forward_stop(source
);
213 /* igmp_source_delete() will stop forwarding source */
214 igmp_source_delete(source
);
217 If there are no more source records for the group, delete group
220 if (!listcount(group
->group_source_list
)) {
221 igmp_group_delete_empty_include(group
);
228 static void source_timer_off(struct igmp_group
*group
,
229 struct igmp_source
*source
)
231 if (!source
->t_source_timer
)
234 if (PIM_DEBUG_IGMP_TRACE
) {
236 char source_str
[100];
237 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
238 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
239 zlog_debug("Cancelling TIMER event for group %s source %s on %s",
240 group_str
, source_str
,
241 group
->group_igmp_sock
->interface
->name
);
244 THREAD_OFF(source
->t_source_timer
);
245 zassert(!source
->t_source_timer
);
248 static void igmp_source_timer_on(struct igmp_group
*group
,
249 struct igmp_source
*source
,
252 source_timer_off(group
, source
);
254 if (PIM_DEBUG_IGMP_EVENTS
) {
256 char source_str
[100];
257 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
258 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
259 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
260 interval_msec
/ 1000,
261 interval_msec
% 1000,
262 group_str
, source_str
,
263 group
->group_igmp_sock
->interface
->name
);
266 THREAD_TIMER_MSEC_ON(master
, source
->t_source_timer
,
268 source
, interval_msec
);
269 zassert(source
->t_source_timer
);
272 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
274 Source timer switched from (T == 0) to (T > 0): enable forwarding.
276 igmp_source_forward_start(source
);
279 void igmp_source_reset_gmi(struct igmp_sock
*igmp
,
280 struct igmp_group
*group
,
281 struct igmp_source
*source
)
283 long group_membership_interval_msec
;
284 struct pim_interface
*pim_ifp
;
285 struct interface
*ifp
;
287 ifp
= igmp
->interface
;
290 group_membership_interval_msec
=
291 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
292 igmp
->querier_query_interval
,
293 pim_ifp
->igmp_query_max_response_time_dsec
);
295 if (PIM_DEBUG_IGMP_TRACE
) {
297 char source_str
[100];
299 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
300 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
302 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
304 group_membership_interval_msec
/ 1000,
305 group_membership_interval_msec
% 1000,
310 igmp_source_timer_on(group
, source
,
311 group_membership_interval_msec
);
314 static void source_mark_delete_flag(struct list
*source_list
)
316 struct listnode
*src_node
;
317 struct igmp_source
*src
;
319 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
320 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
324 static void source_mark_send_flag(struct list
*source_list
)
326 struct listnode
*src_node
;
327 struct igmp_source
*src
;
329 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
330 IGMP_SOURCE_DO_SEND(src
->source_flags
);
334 static int source_mark_send_flag_by_timer(struct list
*source_list
)
336 struct listnode
*src_node
;
337 struct igmp_source
*src
;
338 int num_marked_sources
= 0;
340 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
341 /* Is source timer running? */
342 if (src
->t_source_timer
) {
343 IGMP_SOURCE_DO_SEND(src
->source_flags
);
344 ++num_marked_sources
;
347 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
351 return num_marked_sources
;
354 static void source_clear_send_flag(struct list
*source_list
)
356 struct listnode
*src_node
;
357 struct igmp_source
*src
;
359 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
360 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
365 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
367 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
369 zassert(group
->group_filtermode_isexcl
);
371 if (listcount(group
->group_source_list
) < 1) {
372 igmp_anysource_forward_start(group
);
376 void igmp_source_free(struct igmp_source
*source
)
378 /* make sure there is no source timer running */
379 zassert(!source
->t_source_timer
);
381 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
384 static void source_channel_oil_detach(struct igmp_source
*source
)
386 if (source
->source_channel_oil
) {
387 pim_channel_oil_del(source
->source_channel_oil
);
388 source
->source_channel_oil
= 0;
393 igmp_source_delete: stop fowarding, and delete the source
394 igmp_source_forward_stop: stop fowarding, but keep the source
396 void igmp_source_delete(struct igmp_source
*source
)
398 struct igmp_group
*group
;
400 group
= source
->source_group
;
402 if (PIM_DEBUG_IGMP_TRACE
) {
404 char source_str
[100];
405 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
406 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
407 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
408 source_str
, group_str
,
409 group
->group_igmp_sock
->fd
,
410 group
->group_igmp_sock
->interface
->name
);
413 source_timer_off(group
, source
);
414 igmp_source_forward_stop(source
);
416 /* sanity check that forwarding has been disabled */
417 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
419 char source_str
[100];
420 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
421 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
422 zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
424 source_str
, group_str
,
425 group
->group_igmp_sock
->fd
,
426 group
->group_igmp_sock
->interface
->name
);
430 source_channel_oil_detach(source
);
433 notice that listnode_delete() can't be moved
434 into igmp_source_free() because the later is
435 called by list_delete_all_node()
437 listnode_delete(group
->group_source_list
, source
);
439 igmp_source_free(source
);
441 if (group
->group_filtermode_isexcl
) {
442 group_exclude_fwd_anysrc_ifempty(group
);
446 static void source_delete_by_flag(struct list
*source_list
)
448 struct listnode
*src_node
;
449 struct listnode
*src_nextnode
;
450 struct igmp_source
*src
;
452 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
453 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
454 igmp_source_delete(src
);
457 void igmp_source_delete_expired(struct list
*source_list
)
459 struct listnode
*src_node
;
460 struct listnode
*src_nextnode
;
461 struct igmp_source
*src
;
463 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
464 if (!src
->t_source_timer
)
465 igmp_source_delete(src
);
468 struct igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
469 struct in_addr src_addr
)
471 struct listnode
*src_node
;
472 struct igmp_source
*src
;
474 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
475 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
482 source_new (struct igmp_group
*group
,
483 struct in_addr src_addr
)
485 struct igmp_source
*src
;
487 if (PIM_DEBUG_IGMP_TRACE
) {
489 char source_str
[100];
490 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
491 pim_inet4_dump("<source?>", src_addr
, source_str
, sizeof(source_str
));
492 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
493 source_str
, group_str
,
494 group
->group_igmp_sock
->fd
,
495 group
->group_igmp_sock
->interface
->name
);
498 src
= XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
500 zlog_warn("%s %s: XMALLOC() failure",
501 __FILE__
, __PRETTY_FUNCTION__
);
502 return 0; /* error, not found, could not create */
505 src
->t_source_timer
= NULL
;
506 src
->source_group
= group
; /* back pointer */
507 src
->source_addr
= src_addr
;
508 src
->source_creation
= pim_time_monotonic_sec();
509 src
->source_flags
= 0;
510 src
->source_query_retransmit_count
= 0;
511 src
->source_channel_oil
= NULL
;
513 listnode_add(group
->group_source_list
, src
);
515 zassert(!src
->t_source_timer
); /* source timer == 0 */
517 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
518 igmp_anysource_forward_stop(group
);
523 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
524 struct igmp_group
*group
,
525 struct in_addr src_addr
)
527 struct igmp_source
*src
;
529 src
= igmp_find_source_by_addr(group
, src_addr
);
534 src
= source_new(group
, src_addr
);
542 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
543 struct in_addr group_addr
,
544 int num_sources
, struct in_addr
*sources
)
546 struct igmp_group
*group
;
549 /* non-existant group is created as INCLUDE {empty} */
550 group
= igmp_add_group_by_addr(igmp
, group_addr
);
555 /* scan received sources */
556 for (i
= 0; i
< num_sources
; ++i
) {
557 struct igmp_source
*source
;
558 struct in_addr
*src_addr
;
560 src_addr
= sources
+ i
;
562 source
= add_source_by_addr(igmp
, group
, *src_addr
);
568 RFC 3376: 6.4.1. Reception of Current-State Records
570 When receiving IS_IN reports for groups in EXCLUDE mode is
571 sources should be moved from set with (timers = 0) to set with
574 igmp_source_reset_gmi() below, resetting the source timers to
575 GMI, accomplishes this.
577 igmp_source_reset_gmi(igmp
, group
, source
);
579 } /* scan received sources */
582 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
583 struct in_addr group_addr
,
584 int num_sources
, struct in_addr
*sources
)
586 on_trace(__PRETTY_FUNCTION__
,
587 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
589 allow(igmp
, from
, group_addr
, num_sources
, sources
);
592 static void isex_excl(struct igmp_group
*group
,
593 int num_sources
, struct in_addr
*sources
)
598 zassert(group
->group_filtermode_isexcl
);
600 /* E.1: set deletion flag for known sources (X,Y) */
601 source_mark_delete_flag(group
->group_source_list
);
603 /* scan received sources (A) */
604 for (i
= 0; i
< num_sources
; ++i
) {
605 struct igmp_source
*source
;
606 struct in_addr
*src_addr
;
608 src_addr
= sources
+ i
;
610 /* E.2: lookup reported source from (A) in (X,Y) */
611 source
= igmp_find_source_by_addr(group
, *src_addr
);
613 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
614 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
617 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
618 source
= source_new(group
, *src_addr
);
620 /* ugh, internal malloc failure, skip source */
623 zassert(!source
->t_source_timer
); /* timer == 0 */
624 igmp_source_reset_gmi(group
->group_igmp_sock
, group
, source
);
625 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
628 } /* scan received sources */
630 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
631 source_delete_by_flag(group
->group_source_list
);
634 static void isex_incl(struct igmp_group
*group
,
635 int num_sources
, struct in_addr
*sources
)
640 zassert(!group
->group_filtermode_isexcl
);
642 /* I.1: set deletion flag for known sources (A) */
643 source_mark_delete_flag(group
->group_source_list
);
645 /* scan received sources (B) */
646 for (i
= 0; i
< num_sources
; ++i
) {
647 struct igmp_source
*source
;
648 struct in_addr
*src_addr
;
650 src_addr
= sources
+ i
;
652 /* I.2: lookup reported source (B) */
653 source
= igmp_find_source_by_addr(group
, *src_addr
);
655 /* I.3: if found, clear deletion flag (A*B) */
656 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
659 /* I.4: if not found, create source with timer=0 (B-A) */
660 source
= source_new(group
, *src_addr
);
662 /* ugh, internal malloc failure, skip source */
665 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
668 } /* scan received sources */
670 /* I.5: delete all sources marked with deletion flag (A-B) */
671 source_delete_by_flag(group
->group_source_list
);
673 group
->group_filtermode_isexcl
= 1; /* boolean=true */
675 zassert(group
->group_filtermode_isexcl
);
677 group_exclude_fwd_anysrc_ifempty(group
);
680 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
681 struct in_addr group_addr
,
682 int num_sources
, struct in_addr
*sources
)
684 struct interface
*ifp
= igmp
->interface
;
685 struct igmp_group
*group
;
687 on_trace(__PRETTY_FUNCTION__
,
688 ifp
, from
, group_addr
, num_sources
, sources
);
690 /* non-existant group is created as INCLUDE {empty} */
691 group
= igmp_add_group_by_addr(igmp
, group_addr
);
696 if (group
->group_filtermode_isexcl
) {
698 isex_excl(group
, num_sources
, sources
);
702 isex_incl(group
, num_sources
, sources
);
703 zassert(group
->group_filtermode_isexcl
);
706 zassert(group
->group_filtermode_isexcl
);
708 igmp_group_reset_gmi(group
);
711 static void toin_incl(struct igmp_group
*group
,
712 int num_sources
, struct in_addr
*sources
)
714 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
715 int num_sources_tosend
= listcount(group
->group_source_list
);
718 /* Set SEND flag for all known sources (A) */
719 source_mark_send_flag(group
->group_source_list
);
721 /* Scan received sources (B) */
722 for (i
= 0; i
< num_sources
; ++i
) {
723 struct igmp_source
*source
;
724 struct in_addr
*src_addr
;
726 src_addr
= sources
+ i
;
728 /* Lookup reported source (B) */
729 source
= igmp_find_source_by_addr(group
, *src_addr
);
731 /* If found, clear SEND flag (A*B) */
732 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
733 --num_sources_tosend
;
736 /* If not found, create new source */
737 source
= source_new(group
, *src_addr
);
739 /* ugh, internal malloc failure, skip source */
745 igmp_source_reset_gmi(igmp
, group
, source
);
748 /* Send sources marked with SEND flag: Q(G,A-B) */
749 if (num_sources_tosend
> 0) {
750 source_query_send_by_flag(group
, num_sources_tosend
);
754 static void toin_excl(struct igmp_group
*group
,
755 int num_sources
, struct in_addr
*sources
)
757 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
758 int num_sources_tosend
;
761 /* Set SEND flag for X (sources with timer > 0) */
762 num_sources_tosend
= source_mark_send_flag_by_timer(group
->group_source_list
);
764 /* Scan received sources (A) */
765 for (i
= 0; i
< num_sources
; ++i
) {
766 struct igmp_source
*source
;
767 struct in_addr
*src_addr
;
769 src_addr
= sources
+ i
;
771 /* Lookup reported source (A) */
772 source
= igmp_find_source_by_addr(group
, *src_addr
);
774 if (source
->t_source_timer
) {
775 /* If found and timer running, clear SEND flag (X*A) */
776 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
777 --num_sources_tosend
;
781 /* If not found, create new source */
782 source
= source_new(group
, *src_addr
);
784 /* ugh, internal malloc failure, skip source */
790 igmp_source_reset_gmi(igmp
, group
, source
);
793 /* Send sources marked with SEND flag: Q(G,X-A) */
794 if (num_sources_tosend
> 0) {
795 source_query_send_by_flag(group
, num_sources_tosend
);
799 group_query_send(group
);
802 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
803 struct in_addr group_addr
,
804 int num_sources
, struct in_addr
*sources
)
806 struct interface
*ifp
= igmp
->interface
;
807 struct igmp_group
*group
;
809 on_trace(__PRETTY_FUNCTION__
,
810 ifp
, from
, group_addr
, num_sources
, sources
);
812 /* non-existant group is created as INCLUDE {empty} */
813 group
= igmp_add_group_by_addr(igmp
, group_addr
);
818 if (group
->group_filtermode_isexcl
) {
820 toin_excl(group
, num_sources
, sources
);
824 toin_incl(group
, num_sources
, sources
);
828 static void toex_incl(struct igmp_group
*group
,
829 int num_sources
, struct in_addr
*sources
)
831 int num_sources_tosend
= 0;
834 zassert(!group
->group_filtermode_isexcl
);
836 /* Set DELETE flag for all known sources (A) */
837 source_mark_delete_flag(group
->group_source_list
);
839 /* Clear off SEND flag from all known sources (A) */
840 source_clear_send_flag(group
->group_source_list
);
842 /* Scan received sources (B) */
843 for (i
= 0; i
< num_sources
; ++i
) {
844 struct igmp_source
*source
;
845 struct in_addr
*src_addr
;
847 src_addr
= sources
+ i
;
849 /* Lookup reported source (B) */
850 source
= igmp_find_source_by_addr(group
, *src_addr
);
852 /* If found, clear deletion flag: (A*B) */
853 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
854 /* and set SEND flag (A*B) */
855 IGMP_SOURCE_DO_SEND(source
->source_flags
);
856 ++num_sources_tosend
;
859 /* If source not found, create source with timer=0: (B-A)=0 */
860 source
= source_new(group
, *src_addr
);
862 /* ugh, internal malloc failure, skip source */
865 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
868 } /* Scan received sources (B) */
870 group
->group_filtermode_isexcl
= 1; /* boolean=true */
872 /* Delete all sources marked with DELETE flag (A-B) */
873 source_delete_by_flag(group
->group_source_list
);
875 /* Send sources marked with SEND flag: Q(G,A*B) */
876 if (num_sources_tosend
> 0) {
877 source_query_send_by_flag(group
, num_sources_tosend
);
880 zassert(group
->group_filtermode_isexcl
);
882 group_exclude_fwd_anysrc_ifempty(group
);
885 static void toex_excl(struct igmp_group
*group
,
886 int num_sources
, struct in_addr
*sources
)
888 int num_sources_tosend
= 0;
891 /* set DELETE flag for all known sources (X,Y) */
892 source_mark_delete_flag(group
->group_source_list
);
894 /* clear off SEND flag from all known sources (X,Y) */
895 source_clear_send_flag(group
->group_source_list
);
897 /* scan received sources (A) */
898 for (i
= 0; i
< num_sources
; ++i
) {
899 struct igmp_source
*source
;
900 struct in_addr
*src_addr
;
902 src_addr
= sources
+ i
;
904 /* lookup reported source (A) in known sources (X,Y) */
905 source
= igmp_find_source_by_addr(group
, *src_addr
);
907 /* if found, clear off DELETE flag from reported source (A) */
908 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
911 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
912 long group_timer_msec
;
913 source
= source_new(group
, *src_addr
);
915 /* ugh, internal malloc failure, skip source */
919 zassert(!source
->t_source_timer
); /* timer == 0 */
920 group_timer_msec
= igmp_group_timer_remain_msec(group
);
921 igmp_source_timer_on(group
, source
, group_timer_msec
);
922 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
924 /* make sure source is created with DELETE flag unset */
925 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
928 /* make sure reported source has DELETE flag unset */
929 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
931 if (source
->t_source_timer
) {
932 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
933 IGMP_SOURCE_DO_SEND(source
->source_flags
);
934 ++num_sources_tosend
;
937 } /* scan received sources (A) */
940 delete all sources marked with DELETE flag:
944 source_delete_by_flag(group
->group_source_list
);
946 /* send sources marked with SEND flag: Q(G,A-Y) */
947 if (num_sources_tosend
> 0) {
948 source_query_send_by_flag(group
, num_sources_tosend
);
952 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
953 struct in_addr group_addr
,
954 int num_sources
, struct in_addr
*sources
)
956 struct interface
*ifp
= igmp
->interface
;
957 struct igmp_group
*group
;
959 on_trace(__PRETTY_FUNCTION__
,
960 ifp
, from
, group_addr
, num_sources
, sources
);
962 /* non-existant group is created as INCLUDE {empty} */
963 group
= igmp_add_group_by_addr(igmp
, group_addr
);
968 if (group
->group_filtermode_isexcl
) {
970 toex_excl(group
, num_sources
, sources
);
974 toex_incl(group
, num_sources
, sources
);
975 zassert(group
->group_filtermode_isexcl
);
977 zassert(group
->group_filtermode_isexcl
);
979 /* Group Timer=GMI */
980 igmp_group_reset_gmi(group
);
983 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
984 struct in_addr group_addr
,
985 int num_sources
, struct in_addr
*sources
)
987 on_trace(__PRETTY_FUNCTION__
,
988 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
990 allow(igmp
, from
, group_addr
, num_sources
, sources
);
994 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
996 When transmitting a group specific query, if the group timer is
997 larger than LMQT, the "Suppress Router-Side Processing" bit is set
998 in the query message.
1000 static void group_retransmit_group(struct igmp_group
*group
)
1002 char query_buf
[PIM_IGMP_BUFSIZE_WRITE
];
1003 struct igmp_sock
*igmp
;
1004 struct pim_interface
*pim_ifp
;
1005 long lmqc
; /* Last Member Query Count */
1006 long lmqi_msec
; /* Last Member Query Interval */
1007 long lmqt_msec
; /* Last Member Query Time */
1010 igmp
= group
->group_igmp_sock
;
1011 pim_ifp
= igmp
->interface
->info
;
1013 lmqc
= igmp
->querier_robustness_variable
;
1014 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1015 lmqt_msec
= lmqc
* lmqi_msec
;
1018 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1020 When transmitting a group specific query, if the group timer is
1021 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1022 in the query message.
1024 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1026 if (PIM_DEBUG_IGMP_TRACE
) {
1027 char group_str
[100];
1028 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1029 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1030 group_str
, igmp
->interface
->name
, s_flag
,
1031 group
->group_specific_query_retransmit_count
);
1035 RFC3376: 4.1.12. IP Destination Addresses for Queries
1037 Group-Specific and Group-and-Source-Specific Queries are sent with
1038 an IP destination address equal to the multicast address of
1042 pim_igmp_send_membership_query(group
,
1044 igmp
->interface
->name
,
1047 0 /* num_sources_tosend */,
1048 group
->group_addr
/* dst_addr */,
1049 group
->group_addr
/* group_addr */,
1050 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1052 igmp
->querier_robustness_variable
,
1053 igmp
->querier_query_interval
);
1057 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1059 When building a group and source specific query for a group G, two
1060 separate query messages are sent for the group. The first one has
1061 the "Suppress Router-Side Processing" bit set and contains all the
1062 sources with retransmission state and timers greater than LMQT. The
1063 second has the "Suppress Router-Side Processing" bit clear and
1064 contains all the sources with retransmission state and timers lower
1065 or equal to LMQT. If either of the two calculated messages does not
1066 contain any sources, then its transmission is suppressed.
1068 static int group_retransmit_sources(struct igmp_group
*group
,
1069 int send_with_sflag_set
)
1071 struct igmp_sock
*igmp
;
1072 struct pim_interface
*pim_ifp
;
1073 long lmqc
; /* Last Member Query Count */
1074 long lmqi_msec
; /* Last Member Query Interval */
1075 long lmqt_msec
; /* Last Member Query Time */
1076 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1077 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1078 int query_buf1_max_sources
;
1079 int query_buf2_max_sources
;
1080 struct in_addr
*source_addr1
;
1081 struct in_addr
*source_addr2
;
1082 int num_sources_tosend1
;
1083 int num_sources_tosend2
;
1084 struct listnode
*src_node
;
1085 struct igmp_source
*src
;
1086 int num_retransmit_sources_left
= 0;
1088 query_buf1_max_sources
= (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1089 query_buf2_max_sources
= (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1091 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1092 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1094 igmp
= group
->group_igmp_sock
;
1095 pim_ifp
= igmp
->interface
->info
;
1097 lmqc
= igmp
->querier_robustness_variable
;
1098 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1099 lmqt_msec
= lmqc
* lmqi_msec
;
1101 /* Scan all group sources */
1102 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1104 /* Source has retransmission state? */
1105 if (src
->source_query_retransmit_count
< 1)
1108 if (--src
->source_query_retransmit_count
> 0) {
1109 ++num_retransmit_sources_left
;
1112 /* Copy source address into appropriate query buffer */
1113 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1114 *source_addr1
= src
->source_addr
;
1118 *source_addr2
= src
->source_addr
;
1124 num_sources_tosend1
= source_addr1
- (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1125 num_sources_tosend2
= source_addr2
- (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1127 if (PIM_DEBUG_IGMP_TRACE
) {
1128 char group_str
[100];
1129 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1130 zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
1131 group_str
, igmp
->interface
->name
,
1132 num_sources_tosend1
,
1133 num_sources_tosend2
,
1134 send_with_sflag_set
,
1135 num_retransmit_sources_left
);
1138 if (num_sources_tosend1
> 0) {
1140 Send group-and-source-specific query with s_flag set and all
1141 sources with timers greater than LMQT.
1144 if (send_with_sflag_set
) {
1146 query_buf1_max_sources
= (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1147 if (num_sources_tosend1
> query_buf1_max_sources
) {
1148 char group_str
[100];
1149 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1150 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1151 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1152 num_sources_tosend1
, sizeof(query_buf1
), query_buf1_max_sources
);
1156 RFC3376: 4.1.12. IP Destination Addresses for Queries
1158 Group-Specific and Group-and-Source-Specific Queries are sent with
1159 an IP destination address equal to the multicast address of
1163 pim_igmp_send_membership_query(group
,
1165 igmp
->interface
->name
,
1168 num_sources_tosend1
,
1171 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1173 igmp
->querier_robustness_variable
,
1174 igmp
->querier_query_interval
);
1178 } /* send_with_sflag_set */
1182 if (num_sources_tosend2
> 0) {
1184 Send group-and-source-specific query with s_flag clear and all
1185 sources with timers lower or equal to LMQT.
1188 query_buf2_max_sources
= (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1189 if (num_sources_tosend2
> query_buf2_max_sources
) {
1190 char group_str
[100];
1191 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1192 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1193 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1194 num_sources_tosend2
, sizeof(query_buf2
), query_buf2_max_sources
);
1198 RFC3376: 4.1.12. IP Destination Addresses for Queries
1200 Group-Specific and Group-and-Source-Specific Queries are sent with
1201 an IP destination address equal to the multicast address of
1205 pim_igmp_send_membership_query(group
,
1207 igmp
->interface
->name
,
1210 num_sources_tosend2
,
1213 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1215 igmp
->querier_robustness_variable
,
1216 igmp
->querier_query_interval
);
1221 return num_retransmit_sources_left
;
1224 static int igmp_group_retransmit(struct thread
*t
)
1226 struct igmp_group
*group
;
1227 int num_retransmit_sources_left
;
1228 int send_with_sflag_set
; /* boolean */
1231 group
= THREAD_ARG(t
);
1234 if (PIM_DEBUG_IGMP_TRACE
) {
1235 char group_str
[100];
1236 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1237 zlog_debug("group_retransmit_timer: group %s on %s",
1238 group_str
, group
->group_igmp_sock
->interface
->name
);
1241 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1242 if (group
->group_specific_query_retransmit_count
> 0) {
1244 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1245 group_retransmit_group(group
);
1246 --group
->group_specific_query_retransmit_count
;
1250 If a group specific query is scheduled to be transmitted at the
1251 same time as a group and source specific query for the same group,
1252 then transmission of the group and source specific message with the
1253 "Suppress Router-Side Processing" bit set may be suppressed.
1255 send_with_sflag_set
= 0; /* boolean=false */
1258 send_with_sflag_set
= 1; /* boolean=true */
1261 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1262 num_retransmit_sources_left
= group_retransmit_sources(group
,
1263 send_with_sflag_set
);
1265 group
->t_group_query_retransmit_timer
= 0;
1268 Keep group retransmit timer running if there is any retransmit
1271 if ((num_retransmit_sources_left
> 0) ||
1272 (group
->group_specific_query_retransmit_count
> 0)) {
1273 group_retransmit_timer_on(group
);
1280 group_retransmit_timer_on:
1281 if group retransmit timer isn't running, starts it;
1282 otherwise, do nothing
1284 static void group_retransmit_timer_on(struct igmp_group
*group
)
1286 struct igmp_sock
*igmp
;
1287 struct pim_interface
*pim_ifp
;
1288 long lmqi_msec
; /* Last Member Query Interval */
1290 /* if group retransmit timer is running, do nothing */
1291 if (group
->t_group_query_retransmit_timer
) {
1295 igmp
= group
->group_igmp_sock
;
1296 pim_ifp
= igmp
->interface
->info
;
1298 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1300 if (PIM_DEBUG_IGMP_TRACE
) {
1301 char group_str
[100];
1302 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1303 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1307 igmp
->interface
->name
);
1310 THREAD_TIMER_MSEC_ON(master
, group
->t_group_query_retransmit_timer
,
1311 igmp_group_retransmit
,
1315 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1317 return pim_time_timer_remain_msec(group
->t_group_timer
);
1320 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1322 return pim_time_timer_remain_msec(source
->t_source_timer
);
1326 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1328 static void group_query_send(struct igmp_group
*group
)
1330 long lmqc
; /* Last Member Query Count */
1332 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1334 /* lower group timer to lmqt */
1335 igmp_group_timer_lower_to_lmqt(group
);
1337 /* reset retransmission counter */
1338 group
->group_specific_query_retransmit_count
= lmqc
;
1340 /* immediately send group specific query (decrease retransmit counter by 1)*/
1341 group_retransmit_group(group
);
1343 /* make sure group retransmit timer is running */
1344 group_retransmit_timer_on(group
);
1348 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1350 static void source_query_send_by_flag(struct igmp_group
*group
,
1351 int num_sources_tosend
)
1353 struct igmp_sock
*igmp
;
1354 struct pim_interface
*pim_ifp
;
1355 struct listnode
*src_node
;
1356 struct igmp_source
*src
;
1357 long lmqc
; /* Last Member Query Count */
1358 long lmqi_msec
; /* Last Member Query Interval */
1359 long lmqt_msec
; /* Last Member Query Time */
1361 zassert(num_sources_tosend
> 0);
1363 igmp
= group
->group_igmp_sock
;
1364 pim_ifp
= igmp
->interface
->info
;
1366 lmqc
= igmp
->querier_robustness_variable
;
1367 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1368 lmqt_msec
= lmqc
* lmqi_msec
;
1371 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1373 (...) for each of the sources in X of group G, with source timer larger
1375 o Set number of retransmissions for each source to [Last Member
1377 o Lower source timer to LMQT.
1379 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1380 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1381 /* source "src" in X of group G */
1382 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1383 src
->source_query_retransmit_count
= lmqc
;
1384 igmp_source_timer_lower_to_lmqt(src
);
1389 /* send group-and-source specific queries */
1390 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1392 /* make sure group retransmit timer is running */
1393 group_retransmit_timer_on(group
);
1396 static void block_excl(struct igmp_group
*group
,
1397 int num_sources
, struct in_addr
*sources
)
1399 int num_sources_tosend
= 0;
1402 /* 1. clear off SEND flag from all known sources (X,Y) */
1403 source_clear_send_flag(group
->group_source_list
);
1405 /* 2. scan received sources (A) */
1406 for (i
= 0; i
< num_sources
; ++i
) {
1407 struct igmp_source
*source
;
1408 struct in_addr
*src_addr
;
1410 src_addr
= sources
+ i
;
1412 /* lookup reported source (A) in known sources (X,Y) */
1413 source
= igmp_find_source_by_addr(group
, *src_addr
);
1415 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1416 long group_timer_msec
;
1417 source
= source_new(group
, *src_addr
);
1419 /* ugh, internal malloc failure, skip source */
1423 zassert(!source
->t_source_timer
); /* timer == 0 */
1424 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1425 igmp_source_timer_on(group
, source
, group_timer_msec
);
1426 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1429 if (source
->t_source_timer
) {
1430 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1431 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1432 ++num_sources_tosend
;
1436 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1437 if (num_sources_tosend
> 0) {
1438 source_query_send_by_flag(group
, num_sources_tosend
);
1442 static void block_incl(struct igmp_group
*group
,
1443 int num_sources
, struct in_addr
*sources
)
1445 int num_sources_tosend
= 0;
1448 /* 1. clear off SEND flag from all known sources (B) */
1449 source_clear_send_flag(group
->group_source_list
);
1451 /* 2. scan received sources (A) */
1452 for (i
= 0; i
< num_sources
; ++i
) {
1453 struct igmp_source
*source
;
1454 struct in_addr
*src_addr
;
1456 src_addr
= sources
+ i
;
1458 /* lookup reported source (A) in known sources (B) */
1459 source
= igmp_find_source_by_addr(group
, *src_addr
);
1461 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1462 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1463 ++num_sources_tosend
;
1467 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1468 if (num_sources_tosend
> 0) {
1469 source_query_send_by_flag(group
, num_sources_tosend
);
1473 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1474 struct in_addr group_addr
,
1475 int num_sources
, struct in_addr
*sources
)
1477 struct interface
*ifp
= igmp
->interface
;
1478 struct igmp_group
*group
;
1480 on_trace(__PRETTY_FUNCTION__
,
1481 ifp
, from
, group_addr
, num_sources
, sources
);
1483 /* non-existant group is created as INCLUDE {empty} */
1484 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1489 if (group
->group_filtermode_isexcl
) {
1491 block_excl(group
, num_sources
, sources
);
1495 block_incl(group
, num_sources
, sources
);
1499 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1501 struct igmp_sock
*igmp
;
1502 struct interface
*ifp
;
1503 struct pim_interface
*pim_ifp
;
1505 int lmqi_dsec
; /* Last Member Query Interval */
1506 int lmqc
; /* Last Member Query Count */
1507 int lmqt_msec
; /* Last Member Query Time */
1510 RFC 3376: 6.2.2. Definition of Group Timers
1512 The group timer is only used when a group is in EXCLUDE mode and
1513 it represents the time for the *filter-mode* of the group to
1514 expire and switch to INCLUDE mode.
1516 if (!group
->group_filtermode_isexcl
) {
1520 igmp
= group
->group_igmp_sock
;
1521 ifp
= igmp
->interface
;
1522 pim_ifp
= ifp
->info
;
1525 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1526 lmqc
= igmp
->querier_robustness_variable
;
1527 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1529 if (PIM_DEBUG_IGMP_TRACE
) {
1530 char group_str
[100];
1531 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1532 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1533 __PRETTY_FUNCTION__
,
1535 lmqc
, lmqi_dsec
, lmqt_msec
);
1538 zassert(group
->group_filtermode_isexcl
);
1540 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1543 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1545 struct igmp_group
*group
;
1546 struct igmp_sock
*igmp
;
1547 struct interface
*ifp
;
1548 struct pim_interface
*pim_ifp
;
1550 int lmqi_dsec
; /* Last Member Query Interval */
1551 int lmqc
; /* Last Member Query Count */
1552 int lmqt_msec
; /* Last Member Query Time */
1554 group
= source
->source_group
;
1555 igmp
= group
->group_igmp_sock
;
1556 ifp
= igmp
->interface
;
1557 pim_ifp
= ifp
->info
;
1560 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1561 lmqc
= igmp
->querier_robustness_variable
;
1562 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1564 if (PIM_DEBUG_IGMP_TRACE
) {
1565 char group_str
[100];
1566 char source_str
[100];
1567 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1568 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
1569 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1570 __PRETTY_FUNCTION__
,
1571 group_str
, source_str
, ifname
,
1572 lmqc
, lmqi_dsec
, lmqt_msec
);
1575 igmp_source_timer_on(group
, source
, lmqt_msec
);
1579 Copy sources to message:
1581 struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1582 if (num_sources > 0) {
1583 struct listnode *node;
1584 struct igmp_source *src;
1587 for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1588 sources[i++] = src->source_addr;
1592 void pim_igmp_send_membership_query(struct igmp_group
*group
,
1598 struct in_addr dst_addr
,
1599 struct in_addr group_addr
,
1600 int query_max_response_time_dsec
,
1602 uint8_t querier_robustness_variable
,
1603 uint16_t querier_query_interval
)
1606 uint8_t max_resp_code
;
1609 struct sockaddr_in to
;
1613 zassert(num_sources
>= 0);
1615 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1616 if (msg_size
> query_buf_size
) {
1617 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1618 __FILE__
, __PRETTY_FUNCTION__
,
1619 msg_size
, query_buf_size
);
1623 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1624 zassert((s_flag
== 0) || (s_flag
== 1));
1626 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1627 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1630 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1632 If non-zero, the QRV field contains the [Robustness Variable]
1633 value used by the querier, i.e., the sender of the Query. If the
1634 querier's [Robustness Variable] exceeds 7, the maximum value of
1635 the QRV field, the QRV is set to zero.
1637 if (querier_robustness_variable
> 7) {
1638 querier_robustness_variable
= 0;
1641 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1642 query_buf
[1] = max_resp_code
;
1643 *(uint16_t *)(query_buf
+ IGMP_V3_CHECKSUM_OFFSET
) = 0; /* for computing checksum */
1644 memcpy(query_buf
+4, &group_addr
, sizeof(struct in_addr
));
1646 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1647 query_buf
[9] = qqic
;
1648 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) = htons(num_sources
);
1650 checksum
= in_cksum(query_buf
, msg_size
);
1651 *(uint16_t *)(query_buf
+ IGMP_V3_CHECKSUM_OFFSET
) = checksum
;
1653 if (PIM_DEBUG_IGMP_PACKETS
) {
1655 char group_str
[100];
1656 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1657 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1658 zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
1659 __PRETTY_FUNCTION__
,
1660 dst_str
, ifname
, group_str
, num_sources
,
1661 msg_size
, s_flag
, querier_robustness_variable
,
1662 querier_query_interval
, qqic
, checksum
);
1665 memset(&to
, 0, sizeof(to
));
1666 to
.sin_family
= AF_INET
;
1667 to
.sin_addr
= dst_addr
;
1670 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1671 (struct sockaddr
*)&to
, tolen
);
1672 if (sent
!= (ssize_t
) msg_size
) {
1675 char group_str
[100];
1676 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1677 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1679 zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1680 __PRETTY_FUNCTION__
,
1681 dst_str
, ifname
, group_str
, msg_size
,
1682 e
, safe_strerror(e
));
1685 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
1686 __PRETTY_FUNCTION__
,
1687 dst_str
, ifname
, group_str
,
1694 s_flag sanity test: s_flag must be set for general queries
1696 RFC 3376: 6.6.1. Timer Updates
1698 When a router sends or receives a query with a clear Suppress
1699 Router-Side Processing flag, it must update its timers to reflect
1700 the correct timeout values for the group or sources being queried.
1702 General queries don't trigger timer update.
1705 /* general query? */
1706 if (PIM_INADDR_IS_ANY(group_addr
)) {
1708 char group_str
[100];
1709 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1710 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1711 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1712 __PRETTY_FUNCTION__
,
1713 dst_str
, ifname
, group_str
, num_sources
);