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$ $
28 #include "pim_iface.h"
30 #include "pim_igmpv3.h"
34 #include "pim_zebra.h"
37 static void group_retransmit_timer_on(struct igmp_group *group);
38 static long igmp_group_timer_remain_msec(struct igmp_group *group);
39 static long igmp_source_timer_remain_msec(struct igmp_source *source);
40 static void group_query_send(struct igmp_group *group);
41 static void source_query_send_by_flag(struct igmp_group *group,
42 int num_sources_tosend);
44 static void on_trace(const char *label,
45 struct interface *ifp, struct in_addr from,
46 struct in_addr group_addr,
47 int num_sources, struct in_addr *sources)
49 if (PIM_DEBUG_IGMP_TRACE) {
53 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
54 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
56 zlog_debug("%s: from %s on %s: group=%s sources=%d",
57 label, from_str, ifp->name, group_str, num_sources);
61 int igmp_group_compat_mode(const struct igmp_sock *igmp,
62 const struct igmp_group *group)
64 struct pim_interface *pim_ifp;
66 long older_host_present_interval_dsec;
69 zassert(igmp->interface);
70 zassert(igmp->interface->info);
72 pim_ifp = igmp->interface->info;
75 RFC 3376: 8.13. Older Host Present Interval
77 This value MUST be ((the Robustness Variable) times (the Query
78 Interval)) plus (one Query Response Interval).
80 older_host_present_interval_dsec = \
81 igmp->querier_robustness_variable * \
82 10 * igmp->querier_query_interval + \
83 pim_ifp->query_max_response_time_dsec;
85 older_host_present_interval_dsec =
86 PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
87 igmp->querier_query_interval,
88 pim_ifp->igmp_query_max_response_time_dsec);
90 now_dsec = pim_time_monotonic_dsec();
92 /* broken timer logged by pim_time_monotonic_dsec() */
96 if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
97 return 1; /* IGMPv1 */
99 if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
100 return 2; /* IGMPv2 */
102 return 3; /* IGMPv3 */
105 void igmp_group_reset_gmi(struct igmp_group *group)
107 long group_membership_interval_msec;
108 struct pim_interface *pim_ifp;
109 struct igmp_sock *igmp;
110 struct interface *ifp;
112 igmp = group->group_igmp_sock;
113 ifp = igmp->interface;
117 RFC 3376: 8.4. Group Membership Interval
119 The Group Membership Interval is the amount of time that must pass
120 before a multicast router decides there are no more members of a
121 group or a particular source on a network.
123 This value MUST be ((the Robustness Variable) times (the Query
124 Interval)) plus (one Query Response Interval).
126 group_membership_interval_msec = querier_robustness_variable *
127 (1000 * querier_query_interval) +
128 100 * query_response_interval_dsec;
130 group_membership_interval_msec =
131 PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
132 igmp->querier_query_interval,
133 pim_ifp->igmp_query_max_response_time_dsec);
135 if (PIM_DEBUG_IGMP_TRACE) {
137 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
138 zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
140 group_membership_interval_msec / 1000,
141 group_membership_interval_msec % 1000,
146 RFC 3376: 6.2.2. Definition of Group Timers
148 The group timer is only used when a group is in EXCLUDE mode and
149 it represents the time for the *filter-mode* of the group to
150 expire and switch to INCLUDE mode.
152 zassert(group->group_filtermode_isexcl);
154 igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
157 static int igmp_source_timer(struct thread *t)
159 struct igmp_source *source;
160 struct igmp_group *group;
163 source = THREAD_ARG(t);
166 group = source->source_group;
168 if (PIM_DEBUG_IGMP_TRACE) {
170 char source_str[100];
171 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
172 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
173 zlog_debug("%s: Source timer expired for group %s source %s on %s",
175 group_str, source_str,
176 group->group_igmp_sock->interface->name);
179 zassert(source->t_source_timer);
180 source->t_source_timer = 0;
183 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
186 Filter-Mode Source Timer Value Action
187 ----------- ------------------ ------
188 INCLUDE TIMER == 0 Suggest to stop forwarding
189 traffic from source and
190 remove source record. If
191 there are no more source
192 records for the group, delete
195 EXCLUDE TIMER == 0 Suggest to not forward
197 (DO NOT remove record)
199 Source timer switched from (T > 0) to (T == 0): disable forwarding.
202 zassert(!source->t_source_timer);
204 if (group->group_filtermode_isexcl) {
207 igmp_source_forward_stop(source);
212 /* igmp_source_delete() will stop forwarding source */
213 igmp_source_delete(source);
216 If there are no more source records for the group, delete group
219 if (!listcount(group->group_source_list)) {
220 igmp_group_delete_empty_include(group);
227 static void source_timer_off(struct igmp_group *group,
228 struct igmp_source *source)
230 if (!source->t_source_timer)
233 if (PIM_DEBUG_IGMP_TRACE) {
235 char source_str[100];
236 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
237 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
238 zlog_debug("Cancelling TIMER event for group %s source %s on %s",
239 group_str, source_str,
240 group->group_igmp_sock->interface->name);
243 THREAD_OFF(source->t_source_timer);
244 zassert(!source->t_source_timer);
247 static void igmp_source_timer_on(struct igmp_group *group,
248 struct igmp_source *source,
251 source_timer_off(group, source);
253 if (PIM_DEBUG_IGMP_EVENTS) {
255 char source_str[100];
256 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
257 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
258 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
259 interval_msec / 1000,
260 interval_msec % 1000,
261 group_str, source_str,
262 group->group_igmp_sock->interface->name);
265 THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
267 source, interval_msec);
268 zassert(source->t_source_timer);
271 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
273 Source timer switched from (T == 0) to (T > 0): enable forwarding.
275 igmp_source_forward_start(source);
278 void igmp_source_reset_gmi(struct igmp_sock *igmp,
279 struct igmp_group *group,
280 struct igmp_source *source)
282 long group_membership_interval_msec;
283 struct pim_interface *pim_ifp;
284 struct interface *ifp;
286 ifp = igmp->interface;
289 group_membership_interval_msec =
290 PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
291 igmp->querier_query_interval,
292 pim_ifp->igmp_query_max_response_time_dsec);
294 if (PIM_DEBUG_IGMP_TRACE) {
296 char source_str[100];
298 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
299 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
301 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
303 group_membership_interval_msec / 1000,
304 group_membership_interval_msec % 1000,
309 igmp_source_timer_on(group, source,
310 group_membership_interval_msec);
313 static void source_mark_delete_flag(struct list *source_list)
315 struct listnode *src_node;
316 struct igmp_source *src;
318 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
319 IGMP_SOURCE_DO_DELETE(src->source_flags);
323 static void source_mark_send_flag(struct list *source_list)
325 struct listnode *src_node;
326 struct igmp_source *src;
328 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
329 IGMP_SOURCE_DO_SEND(src->source_flags);
333 static int source_mark_send_flag_by_timer(struct list *source_list)
335 struct listnode *src_node;
336 struct igmp_source *src;
337 int num_marked_sources = 0;
339 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
340 /* Is source timer running? */
341 if (src->t_source_timer) {
342 IGMP_SOURCE_DO_SEND(src->source_flags);
343 ++num_marked_sources;
346 IGMP_SOURCE_DONT_SEND(src->source_flags);
350 return num_marked_sources;
353 static void source_clear_send_flag(struct list *source_list)
355 struct listnode *src_node;
356 struct igmp_source *src;
358 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
359 IGMP_SOURCE_DONT_SEND(src->source_flags);
364 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
366 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
368 zassert(group->group_filtermode_isexcl);
370 if (listcount(group->group_source_list) < 1) {
371 igmp_anysource_forward_start(group);
375 void igmp_source_free(struct igmp_source *source)
377 /* make sure there is no source timer running */
378 zassert(!source->t_source_timer);
380 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
383 static void source_channel_oil_detach(struct igmp_source *source)
385 if (source->source_channel_oil) {
386 pim_channel_oil_del(source->source_channel_oil);
387 source->source_channel_oil = 0;
392 igmp_source_delete: stop fowarding, and delete the source
393 igmp_source_forward_stop: stop fowarding, but keep the source
395 void igmp_source_delete(struct igmp_source *source)
397 struct igmp_group *group;
399 group = source->source_group;
401 if (PIM_DEBUG_IGMP_TRACE) {
403 char source_str[100];
404 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
405 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
406 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
407 source_str, group_str,
408 group->group_igmp_sock->fd,
409 group->group_igmp_sock->interface->name);
412 source_timer_off(group, source);
413 igmp_source_forward_stop(source);
415 /* sanity check that forwarding has been disabled */
416 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
418 char source_str[100];
419 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
420 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
421 zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
423 source_str, group_str,
424 group->group_igmp_sock->fd,
425 group->group_igmp_sock->interface->name);
429 source_channel_oil_detach(source);
432 notice that listnode_delete() can't be moved
433 into igmp_source_free() because the later is
434 called by list_delete_all_node()
436 listnode_delete(group->group_source_list, source);
438 igmp_source_free(source);
440 if (group->group_filtermode_isexcl) {
441 group_exclude_fwd_anysrc_ifempty(group);
445 static void source_delete_by_flag(struct list *source_list)
447 struct listnode *src_node;
448 struct listnode *src_nextnode;
449 struct igmp_source *src;
451 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
452 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
453 igmp_source_delete(src);
456 void igmp_source_delete_expired(struct list *source_list)
458 struct listnode *src_node;
459 struct listnode *src_nextnode;
460 struct igmp_source *src;
462 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
463 if (!src->t_source_timer)
464 igmp_source_delete(src);
467 struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
468 struct in_addr src_addr)
470 struct listnode *src_node;
471 struct igmp_source *src;
473 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
474 if (src_addr.s_addr == src->source_addr.s_addr)
481 source_new (struct igmp_group *group,
482 struct in_addr src_addr)
484 struct igmp_source *src;
486 if (PIM_DEBUG_IGMP_TRACE) {
488 char source_str[100];
489 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
490 pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
491 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
492 source_str, group_str,
493 group->group_igmp_sock->fd,
494 group->group_igmp_sock->interface->name);
497 src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
499 zlog_warn("%s %s: XMALLOC() failure",
500 __FILE__, __PRETTY_FUNCTION__);
501 return 0; /* error, not found, could not create */
504 src->t_source_timer = NULL;
505 src->source_group = group; /* back pointer */
506 src->source_addr = src_addr;
507 src->source_creation = pim_time_monotonic_sec();
508 src->source_flags = 0;
509 src->source_query_retransmit_count = 0;
510 src->source_channel_oil = NULL;
512 listnode_add(group->group_source_list, src);
514 zassert(!src->t_source_timer); /* source timer == 0 */
516 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
517 igmp_anysource_forward_stop(group);
522 static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
523 struct igmp_group *group,
524 struct in_addr src_addr)
526 struct igmp_source *src;
528 src = igmp_find_source_by_addr(group, src_addr);
533 src = source_new(group, src_addr);
541 static void allow(struct igmp_sock *igmp, struct in_addr from,
542 struct in_addr group_addr,
543 int num_sources, struct in_addr *sources)
545 struct igmp_group *group;
548 /* non-existant group is created as INCLUDE {empty} */
549 group = igmp_add_group_by_addr(igmp, group_addr);
554 /* scan received sources */
555 for (i = 0; i < num_sources; ++i) {
556 struct igmp_source *source;
557 struct in_addr *src_addr;
559 src_addr = sources + i;
561 source = add_source_by_addr(igmp, group, *src_addr);
567 RFC 3376: 6.4.1. Reception of Current-State Records
569 When receiving IS_IN reports for groups in EXCLUDE mode is
570 sources should be moved from set with (timers = 0) to set with
573 igmp_source_reset_gmi() below, resetting the source timers to
574 GMI, accomplishes this.
576 igmp_source_reset_gmi(igmp, group, source);
578 } /* scan received sources */
581 void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
582 struct in_addr group_addr,
583 int num_sources, struct in_addr *sources)
585 on_trace(__PRETTY_FUNCTION__,
586 igmp->interface, from, group_addr, num_sources, sources);
588 allow(igmp, from, group_addr, num_sources, sources);
591 static void isex_excl(struct igmp_group *group,
592 int num_sources, struct in_addr *sources)
597 zassert(group->group_filtermode_isexcl);
599 /* E.1: set deletion flag for known sources (X,Y) */
600 source_mark_delete_flag(group->group_source_list);
602 /* scan received sources (A) */
603 for (i = 0; i < num_sources; ++i) {
604 struct igmp_source *source;
605 struct in_addr *src_addr;
607 src_addr = sources + i;
609 /* E.2: lookup reported source from (A) in (X,Y) */
610 source = igmp_find_source_by_addr(group, *src_addr);
612 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
613 IGMP_SOURCE_DONT_DELETE(source->source_flags);
616 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
617 source = source_new(group, *src_addr);
619 /* ugh, internal malloc failure, skip source */
622 zassert(!source->t_source_timer); /* timer == 0 */
623 igmp_source_reset_gmi(group->group_igmp_sock, group, source);
624 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
627 } /* scan received sources */
629 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
630 source_delete_by_flag(group->group_source_list);
633 static void isex_incl(struct igmp_group *group,
634 int num_sources, struct in_addr *sources)
639 zassert(!group->group_filtermode_isexcl);
641 /* I.1: set deletion flag for known sources (A) */
642 source_mark_delete_flag(group->group_source_list);
644 /* scan received sources (B) */
645 for (i = 0; i < num_sources; ++i) {
646 struct igmp_source *source;
647 struct in_addr *src_addr;
649 src_addr = sources + i;
651 /* I.2: lookup reported source (B) */
652 source = igmp_find_source_by_addr(group, *src_addr);
654 /* I.3: if found, clear deletion flag (A*B) */
655 IGMP_SOURCE_DONT_DELETE(source->source_flags);
658 /* I.4: if not found, create source with timer=0 (B-A) */
659 source = source_new(group, *src_addr);
661 /* ugh, internal malloc failure, skip source */
664 zassert(!source->t_source_timer); /* (B-A) timer=0 */
667 } /* scan received sources */
669 /* I.5: delete all sources marked with deletion flag (A-B) */
670 source_delete_by_flag(group->group_source_list);
672 group->group_filtermode_isexcl = 1; /* boolean=true */
674 zassert(group->group_filtermode_isexcl);
676 group_exclude_fwd_anysrc_ifempty(group);
679 void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
680 struct in_addr group_addr,
681 int num_sources, struct in_addr *sources)
683 struct interface *ifp = igmp->interface;
684 struct igmp_group *group;
686 on_trace(__PRETTY_FUNCTION__,
687 ifp, from, group_addr, num_sources, sources);
689 /* non-existant group is created as INCLUDE {empty} */
690 group = igmp_add_group_by_addr(igmp, group_addr);
695 if (group->group_filtermode_isexcl) {
697 isex_excl(group, num_sources, sources);
701 isex_incl(group, num_sources, sources);
702 zassert(group->group_filtermode_isexcl);
705 zassert(group->group_filtermode_isexcl);
707 igmp_group_reset_gmi(group);
710 static void toin_incl(struct igmp_group *group,
711 int num_sources, struct in_addr *sources)
713 struct igmp_sock *igmp = group->group_igmp_sock;
714 int num_sources_tosend = listcount(group->group_source_list);
717 /* Set SEND flag for all known sources (A) */
718 source_mark_send_flag(group->group_source_list);
720 /* Scan received sources (B) */
721 for (i = 0; i < num_sources; ++i) {
722 struct igmp_source *source;
723 struct in_addr *src_addr;
725 src_addr = sources + i;
727 /* Lookup reported source (B) */
728 source = igmp_find_source_by_addr(group, *src_addr);
730 /* If found, clear SEND flag (A*B) */
731 IGMP_SOURCE_DONT_SEND(source->source_flags);
732 --num_sources_tosend;
735 /* If not found, create new source */
736 source = source_new(group, *src_addr);
738 /* ugh, internal malloc failure, skip source */
744 igmp_source_reset_gmi(igmp, group, source);
747 /* Send sources marked with SEND flag: Q(G,A-B) */
748 if (num_sources_tosend > 0) {
749 source_query_send_by_flag(group, num_sources_tosend);
753 static void toin_excl(struct igmp_group *group,
754 int num_sources, struct in_addr *sources)
756 struct igmp_sock *igmp = group->group_igmp_sock;
757 int num_sources_tosend;
760 /* Set SEND flag for X (sources with timer > 0) */
761 num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
763 /* Scan received sources (A) */
764 for (i = 0; i < num_sources; ++i) {
765 struct igmp_source *source;
766 struct in_addr *src_addr;
768 src_addr = sources + i;
770 /* Lookup reported source (A) */
771 source = igmp_find_source_by_addr(group, *src_addr);
773 if (source->t_source_timer) {
774 /* If found and timer running, clear SEND flag (X*A) */
775 IGMP_SOURCE_DONT_SEND(source->source_flags);
776 --num_sources_tosend;
780 /* If not found, create new source */
781 source = source_new(group, *src_addr);
783 /* ugh, internal malloc failure, skip source */
789 igmp_source_reset_gmi(igmp, group, source);
792 /* Send sources marked with SEND flag: Q(G,X-A) */
793 if (num_sources_tosend > 0) {
794 source_query_send_by_flag(group, num_sources_tosend);
798 group_query_send(group);
801 void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
802 struct in_addr group_addr,
803 int num_sources, struct in_addr *sources)
805 struct interface *ifp = igmp->interface;
806 struct igmp_group *group;
808 on_trace(__PRETTY_FUNCTION__,
809 ifp, from, group_addr, num_sources, sources);
811 /* non-existant group is created as INCLUDE {empty} */
812 group = igmp_add_group_by_addr(igmp, group_addr);
817 if (group->group_filtermode_isexcl) {
819 toin_excl(group, num_sources, sources);
823 toin_incl(group, num_sources, sources);
827 static void toex_incl(struct igmp_group *group,
828 int num_sources, struct in_addr *sources)
830 int num_sources_tosend = 0;
833 zassert(!group->group_filtermode_isexcl);
835 /* Set DELETE flag for all known sources (A) */
836 source_mark_delete_flag(group->group_source_list);
838 /* Clear off SEND flag from all known sources (A) */
839 source_clear_send_flag(group->group_source_list);
841 /* Scan received sources (B) */
842 for (i = 0; i < num_sources; ++i) {
843 struct igmp_source *source;
844 struct in_addr *src_addr;
846 src_addr = sources + i;
848 /* Lookup reported source (B) */
849 source = igmp_find_source_by_addr(group, *src_addr);
851 /* If found, clear deletion flag: (A*B) */
852 IGMP_SOURCE_DONT_DELETE(source->source_flags);
853 /* and set SEND flag (A*B) */
854 IGMP_SOURCE_DO_SEND(source->source_flags);
855 ++num_sources_tosend;
858 /* If source not found, create source with timer=0: (B-A)=0 */
859 source = source_new(group, *src_addr);
861 /* ugh, internal malloc failure, skip source */
864 zassert(!source->t_source_timer); /* (B-A) timer=0 */
867 } /* Scan received sources (B) */
869 group->group_filtermode_isexcl = 1; /* boolean=true */
871 /* Delete all sources marked with DELETE flag (A-B) */
872 source_delete_by_flag(group->group_source_list);
874 /* Send sources marked with SEND flag: Q(G,A*B) */
875 if (num_sources_tosend > 0) {
876 source_query_send_by_flag(group, num_sources_tosend);
879 zassert(group->group_filtermode_isexcl);
881 group_exclude_fwd_anysrc_ifempty(group);
884 static void toex_excl(struct igmp_group *group,
885 int num_sources, struct in_addr *sources)
887 int num_sources_tosend = 0;
890 /* set DELETE flag for all known sources (X,Y) */
891 source_mark_delete_flag(group->group_source_list);
893 /* clear off SEND flag from all known sources (X,Y) */
894 source_clear_send_flag(group->group_source_list);
896 /* scan received sources (A) */
897 for (i = 0; i < num_sources; ++i) {
898 struct igmp_source *source;
899 struct in_addr *src_addr;
901 src_addr = sources + i;
903 /* lookup reported source (A) in known sources (X,Y) */
904 source = igmp_find_source_by_addr(group, *src_addr);
906 /* if found, clear off DELETE flag from reported source (A) */
907 IGMP_SOURCE_DONT_DELETE(source->source_flags);
910 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
911 long group_timer_msec;
912 source = source_new(group, *src_addr);
914 /* ugh, internal malloc failure, skip source */
918 zassert(!source->t_source_timer); /* timer == 0 */
919 group_timer_msec = igmp_group_timer_remain_msec(group);
920 igmp_source_timer_on(group, source, group_timer_msec);
921 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
923 /* make sure source is created with DELETE flag unset */
924 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
927 /* make sure reported source has DELETE flag unset */
928 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
930 if (source->t_source_timer) {
931 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
932 IGMP_SOURCE_DO_SEND(source->source_flags);
933 ++num_sources_tosend;
936 } /* scan received sources (A) */
939 delete all sources marked with DELETE flag:
943 source_delete_by_flag(group->group_source_list);
945 /* send sources marked with SEND flag: Q(G,A-Y) */
946 if (num_sources_tosend > 0) {
947 source_query_send_by_flag(group, num_sources_tosend);
951 void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
952 struct in_addr group_addr,
953 int num_sources, struct in_addr *sources)
955 struct interface *ifp = igmp->interface;
956 struct igmp_group *group;
958 on_trace(__PRETTY_FUNCTION__,
959 ifp, from, group_addr, num_sources, sources);
961 /* non-existant group is created as INCLUDE {empty} */
962 group = igmp_add_group_by_addr(igmp, group_addr);
967 if (group->group_filtermode_isexcl) {
969 toex_excl(group, num_sources, sources);
973 toex_incl(group, num_sources, sources);
974 zassert(group->group_filtermode_isexcl);
976 zassert(group->group_filtermode_isexcl);
978 /* Group Timer=GMI */
979 igmp_group_reset_gmi(group);
982 void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
983 struct in_addr group_addr,
984 int num_sources, struct in_addr *sources)
986 on_trace(__PRETTY_FUNCTION__,
987 igmp->interface, from, group_addr, num_sources, sources);
989 allow(igmp, from, group_addr, num_sources, sources);
993 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
995 When transmitting a group specific query, if the group timer is
996 larger than LMQT, the "Suppress Router-Side Processing" bit is set
997 in the query message.
999 static void group_retransmit_group(struct igmp_group *group)
1001 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
1002 struct igmp_sock *igmp;
1003 struct pim_interface *pim_ifp;
1004 long lmqc; /* Last Member Query Count */
1005 long lmqi_msec; /* Last Member Query Interval */
1006 long lmqt_msec; /* Last Member Query Time */
1009 igmp = group->group_igmp_sock;
1010 pim_ifp = igmp->interface->info;
1012 lmqc = igmp->querier_robustness_variable;
1013 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1014 lmqt_msec = lmqc * lmqi_msec;
1017 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1019 When transmitting a group specific query, if the group timer is
1020 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1021 in the query message.
1023 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1025 if (PIM_DEBUG_IGMP_TRACE) {
1026 char group_str[100];
1027 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1028 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1029 group_str, igmp->interface->name, s_flag,
1030 group->group_specific_query_retransmit_count);
1034 RFC3376: 4.1.12. IP Destination Addresses for Queries
1036 Group-Specific and Group-and-Source-Specific Queries are sent with
1037 an IP destination address equal to the multicast address of
1041 pim_igmp_send_membership_query(group,
1043 igmp->interface->name,
1046 0 /* num_sources_tosend */,
1047 group->group_addr /* dst_addr */,
1048 group->group_addr /* group_addr */,
1049 pim_ifp->igmp_specific_query_max_response_time_dsec,
1051 igmp->querier_robustness_variable,
1052 igmp->querier_query_interval);
1056 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1058 When building a group and source specific query for a group G, two
1059 separate query messages are sent for the group. The first one has
1060 the "Suppress Router-Side Processing" bit set and contains all the
1061 sources with retransmission state and timers greater than LMQT. The
1062 second has the "Suppress Router-Side Processing" bit clear and
1063 contains all the sources with retransmission state and timers lower
1064 or equal to LMQT. If either of the two calculated messages does not
1065 contain any sources, then its transmission is suppressed.
1067 static int group_retransmit_sources(struct igmp_group *group,
1068 int send_with_sflag_set)
1070 struct igmp_sock *igmp;
1071 struct pim_interface *pim_ifp;
1072 long lmqc; /* Last Member Query Count */
1073 long lmqi_msec; /* Last Member Query Interval */
1074 long lmqt_msec; /* Last Member Query Time */
1075 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1076 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1077 int query_buf1_max_sources;
1078 int query_buf2_max_sources;
1079 struct in_addr *source_addr1;
1080 struct in_addr *source_addr2;
1081 int num_sources_tosend1;
1082 int num_sources_tosend2;
1083 struct listnode *src_node;
1084 struct igmp_source *src;
1085 int num_retransmit_sources_left = 0;
1087 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1088 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1090 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1091 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1093 igmp = group->group_igmp_sock;
1094 pim_ifp = igmp->interface->info;
1096 lmqc = igmp->querier_robustness_variable;
1097 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1098 lmqt_msec = lmqc * lmqi_msec;
1100 /* Scan all group sources */
1101 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1103 /* Source has retransmission state? */
1104 if (src->source_query_retransmit_count < 1)
1107 if (--src->source_query_retransmit_count > 0) {
1108 ++num_retransmit_sources_left;
1111 /* Copy source address into appropriate query buffer */
1112 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1113 *source_addr1 = src->source_addr;
1117 *source_addr2 = src->source_addr;
1123 num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1124 num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1126 if (PIM_DEBUG_IGMP_TRACE) {
1127 char group_str[100];
1128 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1129 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",
1130 group_str, igmp->interface->name,
1131 num_sources_tosend1,
1132 num_sources_tosend2,
1133 send_with_sflag_set,
1134 num_retransmit_sources_left);
1137 if (num_sources_tosend1 > 0) {
1139 Send group-and-source-specific query with s_flag set and all
1140 sources with timers greater than LMQT.
1143 if (send_with_sflag_set) {
1145 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1146 if (num_sources_tosend1 > query_buf1_max_sources) {
1147 char group_str[100];
1148 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1149 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1150 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1151 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1155 RFC3376: 4.1.12. IP Destination Addresses for Queries
1157 Group-Specific and Group-and-Source-Specific Queries are sent with
1158 an IP destination address equal to the multicast address of
1162 pim_igmp_send_membership_query(group,
1164 igmp->interface->name,
1167 num_sources_tosend1,
1170 pim_ifp->igmp_specific_query_max_response_time_dsec,
1172 igmp->querier_robustness_variable,
1173 igmp->querier_query_interval);
1177 } /* send_with_sflag_set */
1181 if (num_sources_tosend2 > 0) {
1183 Send group-and-source-specific query with s_flag clear and all
1184 sources with timers lower or equal to LMQT.
1187 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1188 if (num_sources_tosend2 > query_buf2_max_sources) {
1189 char group_str[100];
1190 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1191 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1192 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1193 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1197 RFC3376: 4.1.12. IP Destination Addresses for Queries
1199 Group-Specific and Group-and-Source-Specific Queries are sent with
1200 an IP destination address equal to the multicast address of
1204 pim_igmp_send_membership_query(group,
1206 igmp->interface->name,
1209 num_sources_tosend2,
1212 pim_ifp->igmp_specific_query_max_response_time_dsec,
1214 igmp->querier_robustness_variable,
1215 igmp->querier_query_interval);
1220 return num_retransmit_sources_left;
1223 static int igmp_group_retransmit(struct thread *t)
1225 struct igmp_group *group;
1226 int num_retransmit_sources_left;
1227 int send_with_sflag_set; /* boolean */
1230 group = THREAD_ARG(t);
1233 if (PIM_DEBUG_IGMP_TRACE) {
1234 char group_str[100];
1235 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1236 zlog_debug("group_retransmit_timer: group %s on %s",
1237 group_str, group->group_igmp_sock->interface->name);
1240 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1241 if (group->group_specific_query_retransmit_count > 0) {
1243 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1244 group_retransmit_group(group);
1245 --group->group_specific_query_retransmit_count;
1249 If a group specific query is scheduled to be transmitted at the
1250 same time as a group and source specific query for the same group,
1251 then transmission of the group and source specific message with the
1252 "Suppress Router-Side Processing" bit set may be suppressed.
1254 send_with_sflag_set = 0; /* boolean=false */
1257 send_with_sflag_set = 1; /* boolean=true */
1260 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1261 num_retransmit_sources_left = group_retransmit_sources(group,
1262 send_with_sflag_set);
1264 group->t_group_query_retransmit_timer = 0;
1267 Keep group retransmit timer running if there is any retransmit
1270 if ((num_retransmit_sources_left > 0) ||
1271 (group->group_specific_query_retransmit_count > 0)) {
1272 group_retransmit_timer_on(group);
1279 group_retransmit_timer_on:
1280 if group retransmit timer isn't running, starts it;
1281 otherwise, do nothing
1283 static void group_retransmit_timer_on(struct igmp_group *group)
1285 struct igmp_sock *igmp;
1286 struct pim_interface *pim_ifp;
1287 long lmqi_msec; /* Last Member Query Interval */
1289 /* if group retransmit timer is running, do nothing */
1290 if (group->t_group_query_retransmit_timer) {
1294 igmp = group->group_igmp_sock;
1295 pim_ifp = igmp->interface->info;
1297 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1299 if (PIM_DEBUG_IGMP_TRACE) {
1300 char group_str[100];
1301 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1302 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1306 igmp->interface->name);
1309 THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1310 igmp_group_retransmit,
1314 static long igmp_group_timer_remain_msec(struct igmp_group *group)
1316 return pim_time_timer_remain_msec(group->t_group_timer);
1319 static long igmp_source_timer_remain_msec(struct igmp_source *source)
1321 return pim_time_timer_remain_msec(source->t_source_timer);
1325 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1327 static void group_query_send(struct igmp_group *group)
1329 long lmqc; /* Last Member Query Count */
1331 lmqc = group->group_igmp_sock->querier_robustness_variable;
1333 /* lower group timer to lmqt */
1334 igmp_group_timer_lower_to_lmqt(group);
1336 /* reset retransmission counter */
1337 group->group_specific_query_retransmit_count = lmqc;
1339 /* immediately send group specific query (decrease retransmit counter by 1)*/
1340 group_retransmit_group(group);
1342 /* make sure group retransmit timer is running */
1343 group_retransmit_timer_on(group);
1347 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1349 static void source_query_send_by_flag(struct igmp_group *group,
1350 int num_sources_tosend)
1352 struct igmp_sock *igmp;
1353 struct pim_interface *pim_ifp;
1354 struct listnode *src_node;
1355 struct igmp_source *src;
1356 long lmqc; /* Last Member Query Count */
1357 long lmqi_msec; /* Last Member Query Interval */
1358 long lmqt_msec; /* Last Member Query Time */
1360 zassert(num_sources_tosend > 0);
1362 igmp = group->group_igmp_sock;
1363 pim_ifp = igmp->interface->info;
1365 lmqc = igmp->querier_robustness_variable;
1366 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1367 lmqt_msec = lmqc * lmqi_msec;
1370 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1372 (...) for each of the sources in X of group G, with source timer larger
1374 o Set number of retransmissions for each source to [Last Member
1376 o Lower source timer to LMQT.
1378 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1379 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1380 /* source "src" in X of group G */
1381 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1382 src->source_query_retransmit_count = lmqc;
1383 igmp_source_timer_lower_to_lmqt(src);
1388 /* send group-and-source specific queries */
1389 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1391 /* make sure group retransmit timer is running */
1392 group_retransmit_timer_on(group);
1395 static void block_excl(struct igmp_group *group,
1396 int num_sources, struct in_addr *sources)
1398 int num_sources_tosend = 0;
1401 /* 1. clear off SEND flag from all known sources (X,Y) */
1402 source_clear_send_flag(group->group_source_list);
1404 /* 2. scan received sources (A) */
1405 for (i = 0; i < num_sources; ++i) {
1406 struct igmp_source *source;
1407 struct in_addr *src_addr;
1409 src_addr = sources + i;
1411 /* lookup reported source (A) in known sources (X,Y) */
1412 source = igmp_find_source_by_addr(group, *src_addr);
1414 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1415 long group_timer_msec;
1416 source = source_new(group, *src_addr);
1418 /* ugh, internal malloc failure, skip source */
1422 zassert(!source->t_source_timer); /* timer == 0 */
1423 group_timer_msec = igmp_group_timer_remain_msec(group);
1424 igmp_source_timer_on(group, source, group_timer_msec);
1425 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1428 if (source->t_source_timer) {
1429 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1430 IGMP_SOURCE_DO_SEND(source->source_flags);
1431 ++num_sources_tosend;
1435 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1436 if (num_sources_tosend > 0) {
1437 source_query_send_by_flag(group, num_sources_tosend);
1441 static void block_incl(struct igmp_group *group,
1442 int num_sources, struct in_addr *sources)
1444 int num_sources_tosend = 0;
1447 /* 1. clear off SEND flag from all known sources (B) */
1448 source_clear_send_flag(group->group_source_list);
1450 /* 2. scan received sources (A) */
1451 for (i = 0; i < num_sources; ++i) {
1452 struct igmp_source *source;
1453 struct in_addr *src_addr;
1455 src_addr = sources + i;
1457 /* lookup reported source (A) in known sources (B) */
1458 source = igmp_find_source_by_addr(group, *src_addr);
1460 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1461 IGMP_SOURCE_DO_SEND(source->source_flags);
1462 ++num_sources_tosend;
1466 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1467 if (num_sources_tosend > 0) {
1468 source_query_send_by_flag(group, num_sources_tosend);
1472 void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1473 struct in_addr group_addr,
1474 int num_sources, struct in_addr *sources)
1476 struct interface *ifp = igmp->interface;
1477 struct igmp_group *group;
1479 on_trace(__PRETTY_FUNCTION__,
1480 ifp, from, group_addr, num_sources, sources);
1482 /* non-existant group is created as INCLUDE {empty} */
1483 group = igmp_add_group_by_addr(igmp, group_addr);
1488 if (group->group_filtermode_isexcl) {
1490 block_excl(group, num_sources, sources);
1494 block_incl(group, num_sources, sources);
1498 void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1500 struct igmp_sock *igmp;
1501 struct interface *ifp;
1502 struct pim_interface *pim_ifp;
1504 int lmqi_dsec; /* Last Member Query Interval */
1505 int lmqc; /* Last Member Query Count */
1506 int lmqt_msec; /* Last Member Query Time */
1509 RFC 3376: 6.2.2. Definition of Group Timers
1511 The group timer is only used when a group is in EXCLUDE mode and
1512 it represents the time for the *filter-mode* of the group to
1513 expire and switch to INCLUDE mode.
1515 if (!group->group_filtermode_isexcl) {
1519 igmp = group->group_igmp_sock;
1520 ifp = igmp->interface;
1521 pim_ifp = ifp->info;
1524 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1525 lmqc = igmp->querier_robustness_variable;
1526 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1528 if (PIM_DEBUG_IGMP_TRACE) {
1529 char group_str[100];
1530 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1531 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1532 __PRETTY_FUNCTION__,
1534 lmqc, lmqi_dsec, lmqt_msec);
1537 zassert(group->group_filtermode_isexcl);
1539 igmp_group_timer_on(group, lmqt_msec, ifname);
1542 void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1544 struct igmp_group *group;
1545 struct igmp_sock *igmp;
1546 struct interface *ifp;
1547 struct pim_interface *pim_ifp;
1549 int lmqi_dsec; /* Last Member Query Interval */
1550 int lmqc; /* Last Member Query Count */
1551 int lmqt_msec; /* Last Member Query Time */
1553 group = source->source_group;
1554 igmp = group->group_igmp_sock;
1555 ifp = igmp->interface;
1556 pim_ifp = ifp->info;
1559 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1560 lmqc = igmp->querier_robustness_variable;
1561 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1563 if (PIM_DEBUG_IGMP_TRACE) {
1564 char group_str[100];
1565 char source_str[100];
1566 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1567 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1568 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1569 __PRETTY_FUNCTION__,
1570 group_str, source_str, ifname,
1571 lmqc, lmqi_dsec, lmqt_msec);
1574 igmp_source_timer_on(group, source, lmqt_msec);
1578 Copy sources to message:
1580 struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1581 if (num_sources > 0) {
1582 struct listnode *node;
1583 struct igmp_source *src;
1586 for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1587 sources[i++] = src->source_addr;
1591 void pim_igmp_send_membership_query(struct igmp_group *group,
1597 struct in_addr dst_addr,
1598 struct in_addr group_addr,
1599 int query_max_response_time_dsec,
1601 uint8_t querier_robustness_variable,
1602 uint16_t querier_query_interval)
1605 uint8_t max_resp_code;
1608 struct sockaddr_in to;
1612 zassert(num_sources >= 0);
1614 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1615 if (msg_size > query_buf_size) {
1616 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1617 __FILE__, __PRETTY_FUNCTION__,
1618 msg_size, query_buf_size);
1622 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1623 zassert((s_flag == 0) || (s_flag == 1));
1625 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1626 qqic = igmp_msg_encode16to8(querier_query_interval);
1629 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1631 If non-zero, the QRV field contains the [Robustness Variable]
1632 value used by the querier, i.e., the sender of the Query. If the
1633 querier's [Robustness Variable] exceeds 7, the maximum value of
1634 the QRV field, the QRV is set to zero.
1636 if (querier_robustness_variable > 7) {
1637 querier_robustness_variable = 0;
1640 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1641 query_buf[1] = max_resp_code;
1642 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
1643 memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
1645 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1646 query_buf[9] = qqic;
1647 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1649 checksum = in_cksum(query_buf, msg_size);
1650 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1652 if (PIM_DEBUG_IGMP_PACKETS) {
1654 char group_str[100];
1655 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1656 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1657 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",
1658 __PRETTY_FUNCTION__,
1659 dst_str, ifname, group_str, num_sources,
1660 msg_size, s_flag, querier_robustness_variable,
1661 querier_query_interval, qqic, checksum);
1664 memset(&to, 0, sizeof(to));
1665 to.sin_family = AF_INET;
1666 to.sin_addr = dst_addr;
1669 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1670 (struct sockaddr *)&to, tolen);
1671 if (sent != (ssize_t) msg_size) {
1674 char group_str[100];
1675 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1676 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1678 zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1679 __PRETTY_FUNCTION__,
1680 dst_str, ifname, group_str, msg_size,
1681 e, safe_strerror(e));
1684 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
1685 __PRETTY_FUNCTION__,
1686 dst_str, ifname, group_str,
1693 s_flag sanity test: s_flag must be set for general queries
1695 RFC 3376: 6.6.1. Timer Updates
1697 When a router sends or receives a query with a clear Suppress
1698 Router-Side Processing flag, it must update its timers to reflect
1699 the correct timeout values for the group or sources being queried.
1701 General queries don't trigger timer update.
1704 /* general query? */
1705 if (PIM_INADDR_IS_ANY(group_addr)) {
1707 char group_str[100];
1708 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1709 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1710 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1711 __PRETTY_FUNCTION__,
1712 dst_str, ifname, group_str, num_sources);