1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
11 #include "lib_errors.h"
14 #include "pim_instance.h"
15 #include "pim_iface.h"
17 #include "pim_igmpv3.h"
21 #include "pim_zebra.h"
25 static void group_retransmit_timer_on(struct gm_group
*group
);
26 static long igmp_group_timer_remain_msec(struct gm_group
*group
);
27 static long igmp_source_timer_remain_msec(struct gm_source
*source
);
28 static void group_query_send(struct gm_group
*group
);
29 static void source_query_send_by_flag(struct gm_group
*group
,
30 int num_sources_tosend
);
32 static void on_trace(const char *label
, struct interface
*ifp
,
33 struct in_addr from
, struct in_addr group_addr
,
34 int num_sources
, struct in_addr
*sources
)
36 if (PIM_DEBUG_GM_TRACE
) {
37 char from_str
[INET_ADDRSTRLEN
];
38 char group_str
[INET_ADDRSTRLEN
];
40 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
41 pim_inet4_dump("<group?>", group_addr
, group_str
,
44 zlog_debug("%s: from %s on %s: group=%s sources=%d", label
,
45 from_str
, ifp
->name
, group_str
, num_sources
);
49 static inline long igmp_gmi_msec(struct gm_group
*group
)
51 struct pim_interface
*pim_ifp
= group
->interface
->info
;
53 struct listnode
*sock_node
;
55 long qrv
= 0, qqi
= 0;
57 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
58 qrv
= MAX(qrv
, igmp
->querier_robustness_variable
);
59 qqi
= MAX(qqi
, igmp
->querier_query_interval
);
61 return PIM_IGMP_GMI_MSEC(qrv
, qqi
,
62 pim_ifp
->gm_query_max_response_time_dsec
);
65 void igmp_group_reset_gmi(struct gm_group
*group
)
67 long group_membership_interval_msec
;
68 struct interface
*ifp
;
70 ifp
= group
->interface
;
73 RFC 3376: 8.4. Group Membership Interval
75 The Group Membership Interval is the amount of time that must pass
76 before a multicast router decides there are no more members of a
77 group or a particular source on a network.
79 This value MUST be ((the Robustness Variable) times (the Query
80 Interval)) plus (one Query Response Interval).
82 group_membership_interval_msec = querier_robustness_variable *
83 (1000 * querier_query_interval) +
84 100 * query_response_interval_dsec;
86 group_membership_interval_msec
= igmp_gmi_msec(group
);
88 if (PIM_DEBUG_GM_TRACE
) {
89 char group_str
[INET_ADDRSTRLEN
];
90 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
93 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
94 group_str
, group_membership_interval_msec
/ 1000,
95 group_membership_interval_msec
% 1000, ifp
->name
);
99 RFC 3376: 6.2.2. Definition of Group Timers
101 The group timer is only used when a group is in EXCLUDE mode and
102 it represents the time for the *filter-mode* of the group to
103 expire and switch to INCLUDE mode.
105 assert(group
->group_filtermode_isexcl
);
107 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
110 static void igmp_source_timer(struct thread
*t
)
112 struct gm_source
*source
;
113 struct gm_group
*group
;
115 source
= THREAD_ARG(t
);
117 group
= source
->source_group
;
119 if (PIM_DEBUG_GM_TRACE
) {
120 char group_str
[INET_ADDRSTRLEN
];
121 char source_str
[INET_ADDRSTRLEN
];
122 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
124 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
127 "%s: Source timer expired for group %s source %s on %s",
128 __func__
, group_str
, source_str
,
129 group
->interface
->name
);
133 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
136 Filter-Mode Source Timer Value Action
137 ----------- ------------------ ------
138 INCLUDE TIMER == 0 Suggest to stop forwarding
139 traffic from source and
140 remove source record. If
141 there are no more source
142 records for the group, delete
145 EXCLUDE TIMER == 0 Suggest to not forward
147 (DO NOT remove record)
149 Source timer switched from (T > 0) to (T == 0): disable forwarding.
152 if (group
->group_filtermode_isexcl
) {
155 igmp_source_forward_stop(source
);
159 /* igmp_source_delete() will stop forwarding source */
160 igmp_source_delete(source
);
163 If there are no more source records for the group, delete
167 if (!listcount(group
->group_source_list
)) {
168 igmp_group_delete_empty_include(group
);
173 static void source_timer_off(struct gm_group
*group
, struct gm_source
*source
)
175 if (!source
->t_source_timer
)
178 if (PIM_DEBUG_GM_TRACE
) {
179 char group_str
[INET_ADDRSTRLEN
];
180 char source_str
[INET_ADDRSTRLEN
];
181 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
183 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
186 "Cancelling TIMER event for group %s source %s on %s",
187 group_str
, source_str
, group
->interface
->name
);
190 THREAD_OFF(source
->t_source_timer
);
193 static void igmp_source_timer_on(struct gm_group
*group
,
194 struct gm_source
*source
, long interval_msec
)
196 source_timer_off(group
, source
);
197 struct pim_interface
*pim_ifp
= group
->interface
->info
;
199 if (PIM_DEBUG_GM_EVENTS
) {
200 char group_str
[INET_ADDRSTRLEN
];
201 char source_str
[INET_ADDRSTRLEN
];
202 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
204 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
207 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
208 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
209 source_str
, group
->interface
->name
);
212 thread_add_timer_msec(router
->master
, igmp_source_timer
, source
,
213 interval_msec
, &source
->t_source_timer
);
216 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
218 Source timer switched from (T == 0) to (T > 0): enable forwarding.
220 igmp_source_forward_start(pim_ifp
->pim
, source
);
223 void igmp_source_reset_gmi(struct gm_group
*group
, struct gm_source
*source
)
225 long group_membership_interval_msec
;
226 struct interface
*ifp
;
228 ifp
= group
->interface
;
230 group_membership_interval_msec
= igmp_gmi_msec(group
);
232 if (PIM_DEBUG_GM_TRACE
) {
233 char group_str
[INET_ADDRSTRLEN
];
234 char source_str
[INET_ADDRSTRLEN
];
236 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
238 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
242 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
243 source_str
, group_membership_interval_msec
/ 1000,
244 group_membership_interval_msec
% 1000, group_str
,
248 igmp_source_timer_on(group
, source
, group_membership_interval_msec
);
251 static void source_mark_delete_flag(struct gm_group
*group
)
253 struct listnode
*src_node
;
254 struct gm_source
*src
;
256 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
257 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
261 static void source_mark_send_flag(struct gm_group
*group
)
263 struct listnode
*src_node
;
264 struct gm_source
*src
;
266 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
267 IGMP_SOURCE_DO_SEND(src
->source_flags
);
271 static int source_mark_send_flag_by_timer(struct gm_group
*group
)
273 struct listnode
*src_node
;
274 struct gm_source
*src
;
275 int num_marked_sources
= 0;
277 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
278 /* Is source timer running? */
279 if (src
->t_source_timer
) {
280 IGMP_SOURCE_DO_SEND(src
->source_flags
);
281 ++num_marked_sources
;
283 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
287 return num_marked_sources
;
290 static void source_clear_send_flag(struct list
*source_list
)
292 struct listnode
*src_node
;
293 struct gm_source
*src
;
295 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
296 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
301 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
303 static void group_exclude_fwd_anysrc_ifempty(struct gm_group
*group
)
305 struct pim_interface
*pim_ifp
= group
->interface
->info
;
307 assert(group
->group_filtermode_isexcl
);
309 if (listcount(group
->group_source_list
) < 1) {
310 igmp_anysource_forward_start(pim_ifp
->pim
, group
);
314 void igmp_source_free(struct gm_source
*source
)
316 /* make sure there is no source timer running */
317 assert(!source
->t_source_timer
);
319 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
322 static void source_channel_oil_detach(struct gm_source
*source
)
324 if (source
->source_channel_oil
) {
325 pim_channel_oil_del(source
->source_channel_oil
, __func__
);
326 source
->source_channel_oil
= NULL
;
331 igmp_source_delete: stop forwarding, and delete the source
332 igmp_source_forward_stop: stop forwarding, but keep the source
334 void igmp_source_delete(struct gm_source
*source
)
336 struct gm_group
*group
;
339 group
= source
->source_group
;
341 if (PIM_DEBUG_GM_TRACE
) {
342 char group_str
[INET_ADDRSTRLEN
];
343 char source_str
[INET_ADDRSTRLEN
];
344 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
346 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
349 "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d",
350 source_str
, group_str
, group
->interface
->name
,
351 source
->source_channel_oil
352 ? source
->source_channel_oil
->oil_ref_count
356 source_timer_off(group
, source
);
357 igmp_source_forward_stop(source
);
359 /* sanity check that forwarding has been disabled */
360 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
361 char group_str
[INET_ADDRSTRLEN
];
362 char source_str
[INET_ADDRSTRLEN
];
363 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
365 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
368 "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s",
369 __func__
, source_str
, group_str
,
370 group
->interface
->name
);
374 source_channel_oil_detach(source
);
377 notice that listnode_delete() can't be moved
378 into igmp_source_free() because the later is
379 called by list_delete_all_node()
381 listnode_delete(group
->group_source_list
, source
);
383 src
.s_addr
= source
->source_addr
.s_addr
;
384 igmp_source_free(source
);
386 /* Group source list is empty and current source is * then
387 *,G group going away so do not trigger start */
388 if (group
->group_filtermode_isexcl
389 && (listcount(group
->group_source_list
) != 0)
390 && src
.s_addr
!= INADDR_ANY
) {
391 group_exclude_fwd_anysrc_ifempty(group
);
395 static void source_delete_by_flag(struct list
*source_list
)
397 struct listnode
*src_node
;
398 struct listnode
*src_nextnode
;
399 struct gm_source
*src
;
401 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
402 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
403 igmp_source_delete(src
);
406 void igmp_source_delete_expired(struct list
*source_list
)
408 struct listnode
*src_node
;
409 struct listnode
*src_nextnode
;
410 struct gm_source
*src
;
412 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
413 if (!src
->t_source_timer
)
414 igmp_source_delete(src
);
417 struct gm_source
*igmp_find_source_by_addr(struct gm_group
*group
,
418 struct in_addr src_addr
)
420 struct listnode
*src_node
;
421 struct gm_source
*src
;
423 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
424 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
430 struct gm_source
*igmp_get_source_by_addr(struct gm_group
*group
,
431 struct in_addr src_addr
, bool *new)
433 struct gm_source
*src
;
438 src
= igmp_find_source_by_addr(group
, src_addr
);
442 if (PIM_DEBUG_GM_TRACE
) {
443 char group_str
[INET_ADDRSTRLEN
];
444 char source_str
[INET_ADDRSTRLEN
];
445 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
447 pim_inet4_dump("<source?>", src_addr
, source_str
,
450 "Creating new IGMP source %s for group %s on interface %s",
451 source_str
, group_str
, group
->interface
->name
);
454 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
459 src
->t_source_timer
= NULL
;
460 src
->source_group
= group
; /* back pointer */
461 src
->source_addr
= src_addr
;
462 src
->source_creation
= pim_time_monotonic_sec();
463 src
->source_flags
= 0;
464 src
->source_query_retransmit_count
= 0;
465 src
->source_channel_oil
= NULL
;
467 listnode_add(group
->group_source_list
, src
);
469 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
470 igmp_anysource_forward_stop(group
);
474 static void allow(struct gm_sock
*igmp
, struct in_addr from
,
475 struct in_addr group_addr
, int num_sources
,
476 struct in_addr
*sources
)
478 struct gm_source
*source
;
479 struct gm_group
*group
;
482 if (num_sources
== 0) {
484 RFC 3376: 3.1. Socket-State
485 If the requested filter mode is INCLUDE *and* the requested
486 source list is empty, then the entry corresponding to the
487 requested interface and multicast address is deleted if
488 present. If no such entry is present, the request is ignored.
489 So, deleting the group present.
491 group
= find_group_by_addr(igmp
, group_addr
);
495 if (group
->group_filtermode_isexcl
) {
496 if (listcount(group
->group_source_list
) == 1) {
497 struct in_addr star
= {.s_addr
= INADDR_ANY
};
499 source
= igmp_find_source_by_addr(group
, star
);
501 igmp_source_reset_gmi(group
, source
);
504 igmp_group_delete(group
);
510 /* non-existent group is created as INCLUDE {empty} */
511 group
= igmp_add_group_by_addr(igmp
, group_addr
);
516 /* scan received sources */
517 for (i
= 0; i
< num_sources
; ++i
) {
518 struct in_addr
*src_addr
;
520 src_addr
= sources
+ i
;
522 source
= igmp_get_source_by_addr(group
, *src_addr
, NULL
);
527 RFC 3376: 6.4.1. Reception of Current-State Records
529 When receiving IS_IN reports for groups in EXCLUDE mode is
530 sources should be moved from set with (timers = 0) to set with
533 igmp_source_reset_gmi() below, resetting the source timers to
534 GMI, accomplishes this.
536 igmp_source_reset_gmi(group
, source
);
538 } /* scan received sources */
541 void igmpv3_report_isin(struct gm_sock
*igmp
, struct in_addr from
,
542 struct in_addr group_addr
, int num_sources
,
543 struct in_addr
*sources
)
545 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
548 allow(igmp
, from
, group_addr
, num_sources
, sources
);
551 static void isex_excl(struct gm_group
*group
, int num_sources
,
552 struct in_addr
*sources
)
554 struct gm_source
*source
;
558 assert(group
->group_filtermode_isexcl
);
560 /* E.1: set deletion flag for known sources (X,Y) */
561 source_mark_delete_flag(group
);
563 /* scan received sources (A) */
564 for (i
= 0; i
< num_sources
; ++i
) {
565 struct in_addr
*src_addr
;
568 src_addr
= sources
+ i
;
570 /* E.2: lookup reported source from (A) in (X,Y) */
571 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
576 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
577 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
579 /* E.4: if not found, create source with timer=GMI:
581 assert(!source
->t_source_timer
); /* timer == 0 */
582 igmp_source_reset_gmi(group
, source
);
583 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
586 } /* scan received sources */
589 * If we are in isexcl mode and num_sources == 0
590 * than that means we have a *,g entry that
591 * needs to be handled
593 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
594 struct in_addr star
= {.s_addr
= INADDR_ANY
};
595 source
= igmp_find_source_by_addr(group
, star
);
597 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
598 igmp_source_reset_gmi(group
, source
);
602 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
603 source_delete_by_flag(group
->group_source_list
);
606 static void isex_incl(struct gm_group
*group
, int num_sources
,
607 struct in_addr
*sources
)
612 assert(!group
->group_filtermode_isexcl
);
614 /* I.1: set deletion flag for known sources (A) */
615 source_mark_delete_flag(group
);
617 /* scan received sources (B) */
618 for (i
= 0; i
< num_sources
; ++i
) {
619 struct gm_source
*source
;
620 struct in_addr
*src_addr
;
623 src_addr
= sources
+ i
;
625 /* I.2: lookup reported source (B) */
626 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
631 /* I.3: if found, clear deletion flag (A*B) */
632 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
634 /* I.4: if not found, create source with timer=0 (B-A)
636 assert(!source
->t_source_timer
); /* (B-A) timer=0 */
639 } /* scan received sources */
641 /* I.5: delete all sources marked with deletion flag (A-B) */
642 source_delete_by_flag(group
->group_source_list
);
644 group
->group_filtermode_isexcl
= 1; /* boolean=true */
646 assert(group
->group_filtermode_isexcl
);
648 group_exclude_fwd_anysrc_ifempty(group
);
651 void igmpv3_report_isex(struct gm_sock
*igmp
, struct in_addr from
,
652 struct in_addr group_addr
, int num_sources
,
653 struct in_addr
*sources
, int from_igmp_v2_report
)
655 struct interface
*ifp
= igmp
->interface
;
656 struct gm_group
*group
;
658 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
660 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
663 /* non-existent group is created as INCLUDE {empty} */
664 group
= igmp_add_group_by_addr(igmp
, group_addr
);
669 /* So we can display how we learned the group in our show command output
671 if (from_igmp_v2_report
)
672 group
->igmp_version
= 2;
674 if (group
->group_filtermode_isexcl
) {
676 isex_excl(group
, num_sources
, sources
);
679 isex_incl(group
, num_sources
, sources
);
680 assert(group
->group_filtermode_isexcl
);
683 assert(group
->group_filtermode_isexcl
);
685 igmp_group_reset_gmi(group
);
688 static void toin_incl(struct gm_group
*group
, int num_sources
,
689 struct in_addr
*sources
)
691 int num_sources_tosend
= listcount(group
->group_source_list
);
694 /* Set SEND flag for all known sources (A) */
695 source_mark_send_flag(group
);
697 /* Scan received sources (B) */
698 for (i
= 0; i
< num_sources
; ++i
) {
699 struct gm_source
*source
;
700 struct in_addr
*src_addr
;
703 src_addr
= sources
+ i
;
705 /* Lookup reported source (B) */
706 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
711 /* If found, clear SEND flag (A*B) */
712 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
713 --num_sources_tosend
;
717 igmp_source_reset_gmi(group
, source
);
720 /* Send sources marked with SEND flag: Q(G,A-B) */
721 if (num_sources_tosend
> 0) {
722 source_query_send_by_flag(group
, num_sources_tosend
);
726 static void toin_excl(struct gm_group
*group
, int num_sources
,
727 struct in_addr
*sources
)
729 int num_sources_tosend
;
732 /* Set SEND flag for X (sources with timer > 0) */
733 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
735 /* Scan received sources (A) */
736 for (i
= 0; i
< num_sources
; ++i
) {
737 struct gm_source
*source
;
738 struct in_addr
*src_addr
;
741 src_addr
= sources
+ i
;
743 /* Lookup reported source (A) */
744 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
748 if (source
->t_source_timer
) {
749 /* If found and timer running, clear SEND flag
751 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
752 --num_sources_tosend
;
756 igmp_source_reset_gmi(group
, source
);
759 /* Send sources marked with SEND flag: Q(G,X-A) */
760 if (num_sources_tosend
> 0) {
761 source_query_send_by_flag(group
, num_sources_tosend
);
765 group_query_send(group
);
768 void igmpv3_report_toin(struct gm_sock
*igmp
, struct in_addr from
,
769 struct in_addr group_addr
, int num_sources
,
770 struct in_addr
*sources
)
772 struct interface
*ifp
= igmp
->interface
;
773 struct gm_group
*group
;
775 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
778 * If the requested filter mode is INCLUDE *and* the requested source
779 * list is empty, then the entry corresponding to the requested
780 * interface and multicast address is deleted if present. If no such
781 * entry is present, the request is ignored.
784 /* non-existent group is created as INCLUDE {empty} */
785 group
= igmp_add_group_by_addr(igmp
, group_addr
);
790 group
= find_group_by_addr(igmp
, group_addr
);
795 if (group
->group_filtermode_isexcl
) {
797 toin_excl(group
, num_sources
, sources
);
800 toin_incl(group
, num_sources
, sources
);
804 static void toex_incl(struct gm_group
*group
, int num_sources
,
805 struct in_addr
*sources
)
807 int num_sources_tosend
= 0;
810 assert(!group
->group_filtermode_isexcl
);
812 /* Set DELETE flag for all known sources (A) */
813 source_mark_delete_flag(group
);
815 /* Clear off SEND flag from all known sources (A) */
816 source_clear_send_flag(group
->group_source_list
);
818 /* Scan received sources (B) */
819 for (i
= 0; i
< num_sources
; ++i
) {
820 struct gm_source
*source
;
821 struct in_addr
*src_addr
;
824 src_addr
= sources
+ i
;
826 /* Lookup reported source (B) */
827 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
829 /* If found, clear deletion flag: (A*B) */
830 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
831 /* and set SEND flag (A*B) */
832 IGMP_SOURCE_DO_SEND(source
->source_flags
);
833 ++num_sources_tosend
;
836 } /* Scan received sources (B) */
838 group
->group_filtermode_isexcl
= 1; /* boolean=true */
840 /* Delete all sources marked with DELETE flag (A-B) */
841 source_delete_by_flag(group
->group_source_list
);
843 /* Send sources marked with SEND flag: Q(G,A*B) */
844 if (num_sources_tosend
> 0) {
845 source_query_send_by_flag(group
, num_sources_tosend
);
848 assert(group
->group_filtermode_isexcl
);
850 group_exclude_fwd_anysrc_ifempty(group
);
853 static void toex_excl(struct gm_group
*group
, int num_sources
,
854 struct in_addr
*sources
)
856 int num_sources_tosend
= 0;
859 /* set DELETE flag for all known sources (X,Y) */
860 source_mark_delete_flag(group
);
862 /* clear off SEND flag from all known sources (X,Y) */
863 source_clear_send_flag(group
->group_source_list
);
865 if (num_sources
== 0) {
866 struct gm_source
*source
;
867 struct in_addr any
= {.s_addr
= INADDR_ANY
};
869 source
= igmp_find_source_by_addr(group
, any
);
871 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
874 /* scan received sources (A) */
875 for (i
= 0; i
< num_sources
; ++i
) {
876 struct gm_source
*source
;
877 struct in_addr
*src_addr
;
880 src_addr
= sources
+ i
;
882 /* lookup reported source (A) in known sources (X,Y) */
883 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
888 /* if found, clear off DELETE flag from reported source
890 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
892 /* if not found, create source with Group Timer:
893 * (A-X-Y)=Group Timer */
894 long group_timer_msec
;
896 assert(!source
->t_source_timer
); /* timer == 0 */
897 group_timer_msec
= igmp_group_timer_remain_msec(group
);
898 igmp_source_timer_on(group
, source
, group_timer_msec
);
899 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
901 /* make sure source is created with DELETE flag unset */
902 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
905 /* make sure reported source has DELETE flag unset */
906 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
908 if (source
->t_source_timer
) {
909 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
910 IGMP_SOURCE_DO_SEND(source
->source_flags
);
911 ++num_sources_tosend
;
914 } /* scan received sources (A) */
917 delete all sources marked with DELETE flag:
921 source_delete_by_flag(group
->group_source_list
);
923 /* send sources marked with SEND flag: Q(G,A-Y) */
924 if (num_sources_tosend
> 0) {
925 source_query_send_by_flag(group
, num_sources_tosend
);
929 void igmpv3_report_toex(struct gm_sock
*igmp
, struct in_addr from
,
930 struct in_addr group_addr
, int num_sources
,
931 struct in_addr
*sources
)
933 struct interface
*ifp
= igmp
->interface
;
934 struct gm_group
*group
;
936 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
938 /* non-existent group is created as INCLUDE {empty} */
939 group
= igmp_add_group_by_addr(igmp
, group_addr
);
944 if (group
->group_filtermode_isexcl
) {
946 toex_excl(group
, num_sources
, sources
);
949 toex_incl(group
, num_sources
, sources
);
950 assert(group
->group_filtermode_isexcl
);
952 assert(group
->group_filtermode_isexcl
);
954 /* Group Timer=GMI */
955 igmp_group_reset_gmi(group
);
958 void igmpv3_report_allow(struct gm_sock
*igmp
, struct in_addr from
,
959 struct in_addr group_addr
, int num_sources
,
960 struct in_addr
*sources
)
962 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
965 allow(igmp
, from
, group_addr
, num_sources
, sources
);
968 static void igmp_send_query_group(struct gm_group
*group
, char *query_buf
,
969 size_t query_buf_size
, int num_sources
,
972 struct interface
*ifp
= group
->interface
;
973 struct pim_interface
*pim_ifp
= ifp
->info
;
974 struct gm_sock
*igmp
;
975 struct listnode
*sock_node
;
977 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
979 pim_ifp
->igmp_version
, group
, query_buf
, query_buf_size
,
980 num_sources
, group
->group_addr
, group
->group_addr
,
981 pim_ifp
->gm_specific_query_max_response_time_dsec
,
987 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
989 When transmitting a group specific query, if the group timer is
990 larger than LMQT, the "Suppress Router-Side Processing" bit is set
991 in the query message.
993 static void group_retransmit_group(struct gm_group
*group
)
995 struct pim_interface
*pim_ifp
;
996 long lmqc
; /* Last Member Query Count */
997 long lmqi_msec
; /* Last Member Query Interval */
998 long lmqt_msec
; /* Last Member Query Time */
1002 pim_ifp
= group
->interface
->info
;
1004 if (pim_ifp
->igmp_version
== 3) {
1005 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1007 query_buf_size
= IGMP_V12_MSG_SIZE
;
1010 char query_buf
[query_buf_size
];
1012 lmqc
= pim_ifp
->gm_last_member_query_count
;
1013 lmqi_msec
= 100 * pim_ifp
->gm_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_GM_TRACE
) {
1026 char group_str
[INET_ADDRSTRLEN
];
1027 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1030 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1031 group_str
, group
->interface
->name
, s_flag
,
1032 group
->group_specific_query_retransmit_count
);
1036 RFC3376: 4.1.12. IP Destination Addresses for Queries
1038 Group-Specific and Group-and-Source-Specific Queries are sent with
1039 an IP destination address equal to the multicast address of
1043 igmp_send_query_group(group
, query_buf
, sizeof(query_buf
), 0, s_flag
);
1047 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1049 When building a group and source specific query for a group G, two
1050 separate query messages are sent for the group. The first one has
1051 the "Suppress Router-Side Processing" bit set and contains all the
1052 sources with retransmission state and timers greater than LMQT. The
1053 second has the "Suppress Router-Side Processing" bit clear and
1054 contains all the sources with retransmission state and timers lower
1055 or equal to LMQT. If either of the two calculated messages does not
1056 contain any sources, then its transmission is suppressed.
1058 static int group_retransmit_sources(struct gm_group
*group
,
1059 int send_with_sflag_set
)
1061 struct pim_interface
*pim_ifp
;
1062 long lmqc
; /* Last Member Query Count */
1063 long lmqi_msec
; /* Last Member Query Interval */
1064 long lmqt_msec
; /* Last Member Query Time */
1065 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1066 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1067 int query_buf1_max_sources
;
1068 int query_buf2_max_sources
;
1069 struct in_addr
*source_addr1
;
1070 struct in_addr
*source_addr2
;
1071 int num_sources_tosend1
;
1072 int num_sources_tosend2
;
1073 struct listnode
*src_node
;
1074 struct gm_source
*src
;
1075 int num_retransmit_sources_left
= 0;
1077 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1078 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1080 pim_ifp
= group
->interface
->info
;
1082 lmqc
= pim_ifp
->gm_last_member_query_count
;
1083 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1084 lmqt_msec
= lmqc
* lmqi_msec
;
1086 /* Scan all group sources */
1087 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1089 /* Source has retransmission state? */
1090 if (src
->source_query_retransmit_count
< 1)
1093 if (--src
->source_query_retransmit_count
> 0) {
1094 ++num_retransmit_sources_left
;
1097 /* Copy source address into appropriate query buffer */
1098 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1099 *source_addr1
= src
->source_addr
;
1102 *source_addr2
= src
->source_addr
;
1107 num_sources_tosend1
=
1109 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1110 num_sources_tosend2
=
1112 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1114 if (PIM_DEBUG_GM_TRACE
) {
1115 char group_str
[INET_ADDRSTRLEN
];
1116 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1119 "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",
1120 group_str
, group
->interface
->name
, num_sources_tosend1
,
1121 num_sources_tosend2
, send_with_sflag_set
,
1122 num_retransmit_sources_left
);
1125 if (num_sources_tosend1
> 0) {
1127 Send group-and-source-specific query with s_flag set and all
1128 sources with timers greater than LMQT.
1131 if (send_with_sflag_set
) {
1133 query_buf1_max_sources
=
1134 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1136 if (num_sources_tosend1
> query_buf1_max_sources
) {
1137 char group_str
[INET_ADDRSTRLEN
];
1138 pim_inet4_dump("<group?>", group
->group_addr
,
1139 group_str
, sizeof(group_str
));
1141 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1142 __func__
, group_str
,
1143 group
->interface
->name
,
1144 num_sources_tosend1
, sizeof(query_buf1
),
1145 query_buf1_max_sources
);
1148 RFC3376: 4.1.12. IP Destination Addresses for
1151 Group-Specific and Group-and-Source-Specific
1152 Queries are sent with
1153 an IP destination address equal to the
1154 multicast address of
1158 igmp_send_query_group(
1159 group
, query_buf1
, sizeof(query_buf1
),
1160 num_sources_tosend1
, 1 /* s_flag */);
1163 } /* send_with_sflag_set */
1166 if (num_sources_tosend2
> 0) {
1168 Send group-and-source-specific query with s_flag clear and all
1169 sources with timers lower or equal to LMQT.
1172 query_buf2_max_sources
=
1173 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1174 if (num_sources_tosend2
> query_buf2_max_sources
) {
1175 char group_str
[INET_ADDRSTRLEN
];
1176 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1179 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1180 __func__
, group_str
, group
->interface
->name
,
1181 num_sources_tosend2
, sizeof(query_buf2
),
1182 query_buf2_max_sources
);
1185 RFC3376: 4.1.12. IP Destination Addresses for Queries
1187 Group-Specific and Group-and-Source-Specific Queries
1189 an IP destination address equal to the multicast
1194 igmp_send_query_group(
1195 group
, query_buf2
, sizeof(query_buf2
),
1196 num_sources_tosend2
, 0 /* s_flag */);
1200 return num_retransmit_sources_left
;
1203 static void igmp_group_retransmit(struct thread
*t
)
1205 struct gm_group
*group
;
1206 int num_retransmit_sources_left
;
1207 int send_with_sflag_set
; /* boolean */
1209 group
= THREAD_ARG(t
);
1211 if (PIM_DEBUG_GM_TRACE
) {
1212 char group_str
[INET_ADDRSTRLEN
];
1213 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1215 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1216 group
->interface
->name
);
1219 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1220 if (group
->group_specific_query_retransmit_count
> 0) {
1222 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1223 group_retransmit_group(group
);
1224 --group
->group_specific_query_retransmit_count
;
1228 If a group specific query is scheduled to be transmitted at
1230 same time as a group and source specific query for the same
1232 then transmission of the group and source specific message
1234 "Suppress Router-Side Processing" bit set may be suppressed.
1236 send_with_sflag_set
= 0; /* boolean=false */
1238 send_with_sflag_set
= 1; /* boolean=true */
1241 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1242 num_retransmit_sources_left
=
1243 group_retransmit_sources(group
, send_with_sflag_set
);
1246 Keep group retransmit timer running if there is any retransmit
1249 if ((num_retransmit_sources_left
> 0)
1250 || (group
->group_specific_query_retransmit_count
> 0)) {
1251 group_retransmit_timer_on(group
);
1256 group_retransmit_timer_on:
1257 if group retransmit timer isn't running, starts it;
1258 otherwise, do nothing
1260 static void group_retransmit_timer_on(struct gm_group
*group
)
1262 struct pim_interface
*pim_ifp
;
1263 long lmqi_msec
; /* Last Member Query Interval */
1265 /* if group retransmit timer is running, do nothing */
1266 if (group
->t_group_query_retransmit_timer
) {
1270 pim_ifp
= group
->interface
->info
;
1272 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1274 if (PIM_DEBUG_GM_TRACE
) {
1275 char group_str
[INET_ADDRSTRLEN
];
1276 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1279 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1280 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1281 group
->interface
->name
);
1284 thread_add_timer_msec(router
->master
, igmp_group_retransmit
, group
,
1286 &group
->t_group_query_retransmit_timer
);
1289 static long igmp_group_timer_remain_msec(struct gm_group
*group
)
1291 return pim_time_timer_remain_msec(group
->t_group_timer
);
1294 static long igmp_source_timer_remain_msec(struct gm_source
*source
)
1296 return pim_time_timer_remain_msec(source
->t_source_timer
);
1300 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1302 static void group_query_send(struct gm_group
*group
)
1304 struct pim_interface
*pim_ifp
;
1305 long lmqc
; /* Last Member Query Count */
1307 pim_ifp
= group
->interface
->info
;
1308 lmqc
= pim_ifp
->gm_last_member_query_count
;
1310 /* lower group timer to lmqt */
1311 igmp_group_timer_lower_to_lmqt(group
);
1313 /* reset retransmission counter */
1314 group
->group_specific_query_retransmit_count
= lmqc
;
1316 /* immediately send group specific query (decrease retransmit counter by
1318 group_retransmit_group(group
);
1320 /* make sure group retransmit timer is running */
1321 group_retransmit_timer_on(group
);
1325 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1327 static void source_query_send_by_flag(struct gm_group
*group
,
1328 int num_sources_tosend
)
1330 struct pim_interface
*pim_ifp
;
1331 struct listnode
*src_node
;
1332 struct gm_source
*src
;
1333 long lmqc
; /* Last Member Query Count */
1334 long lmqi_msec
; /* Last Member Query Interval */
1335 long lmqt_msec
; /* Last Member Query Time */
1337 assert(num_sources_tosend
> 0);
1339 pim_ifp
= group
->interface
->info
;
1341 lmqc
= pim_ifp
->gm_last_member_query_count
;
1342 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1343 lmqt_msec
= lmqc
* lmqi_msec
;
1346 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1349 (...) for each of the sources in X of group G, with source timer
1352 o Set number of retransmissions for each source to [Last Member
1354 o Lower source timer to LMQT.
1356 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1357 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1358 /* source "src" in X of group G */
1359 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1360 src
->source_query_retransmit_count
= lmqc
;
1361 igmp_source_timer_lower_to_lmqt(src
);
1366 /* send group-and-source specific queries */
1367 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1369 /* make sure group retransmit timer is running */
1370 group_retransmit_timer_on(group
);
1373 static void block_excl(struct gm_group
*group
, int num_sources
,
1374 struct in_addr
*sources
)
1376 int num_sources_tosend
= 0;
1379 /* 1. clear off SEND flag from all known sources (X,Y) */
1380 source_clear_send_flag(group
->group_source_list
);
1382 /* 2. scan received sources (A) */
1383 for (i
= 0; i
< num_sources
; ++i
) {
1384 struct gm_source
*source
;
1385 struct in_addr
*src_addr
;
1388 src_addr
= sources
+ i
;
1390 /* lookup reported source (A) in known sources (X,Y) */
1391 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
1396 /* 3: if not found, create source with Group Timer:
1397 * (A-X-Y)=Group Timer */
1398 long group_timer_msec
;
1400 assert(!source
->t_source_timer
); /* timer == 0 */
1401 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1402 igmp_source_timer_on(group
, source
, group_timer_msec
);
1403 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1406 if (source
->t_source_timer
) {
1407 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1408 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1409 ++num_sources_tosend
;
1413 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1414 if (num_sources_tosend
> 0) {
1415 source_query_send_by_flag(group
, num_sources_tosend
);
1419 static void block_incl(struct gm_group
*group
, int num_sources
,
1420 struct in_addr
*sources
)
1422 int num_sources_tosend
= 0;
1425 /* 1. clear off SEND flag from all known sources (B) */
1426 source_clear_send_flag(group
->group_source_list
);
1428 /* 2. scan received sources (A) */
1429 for (i
= 0; i
< num_sources
; ++i
) {
1430 struct gm_source
*source
;
1431 struct in_addr
*src_addr
;
1433 src_addr
= sources
+ i
;
1435 /* lookup reported source (A) in known sources (B) */
1436 source
= igmp_find_source_by_addr(group
, *src_addr
);
1438 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1439 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1440 ++num_sources_tosend
;
1444 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1445 if (num_sources_tosend
> 0) {
1446 source_query_send_by_flag(group
, num_sources_tosend
);
1450 void igmpv3_report_block(struct gm_sock
*igmp
, struct in_addr from
,
1451 struct in_addr group_addr
, int num_sources
,
1452 struct in_addr
*sources
)
1454 struct interface
*ifp
= igmp
->interface
;
1455 struct gm_group
*group
;
1457 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
1459 /* non-existent group is created as INCLUDE {empty} */
1460 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1465 if (group
->group_filtermode_isexcl
) {
1467 block_excl(group
, num_sources
, sources
);
1470 block_incl(group
, num_sources
, sources
);
1474 void igmp_group_timer_lower_to_lmqt(struct gm_group
*group
)
1476 struct interface
*ifp
;
1477 struct pim_interface
*pim_ifp
;
1479 int lmqi_dsec
; /* Last Member Query Interval */
1480 int lmqc
; /* Last Member Query Count */
1481 int lmqt_msec
; /* Last Member Query Time */
1484 RFC 3376: 6.2.2. Definition of Group Timers
1486 The group timer is only used when a group is in EXCLUDE mode and
1487 it represents the time for the *filter-mode* of the group to
1488 expire and switch to INCLUDE mode.
1490 if (!group
->group_filtermode_isexcl
) {
1494 ifp
= group
->interface
;
1495 pim_ifp
= ifp
->info
;
1498 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1499 lmqc
= pim_ifp
->gm_last_member_query_count
;
1500 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1501 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1503 if (PIM_DEBUG_GM_TRACE
) {
1504 char group_str
[INET_ADDRSTRLEN
];
1505 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1508 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1509 __func__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1513 assert(group
->group_filtermode_isexcl
);
1515 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1518 void igmp_source_timer_lower_to_lmqt(struct gm_source
*source
)
1520 struct gm_group
*group
;
1521 struct interface
*ifp
;
1522 struct pim_interface
*pim_ifp
;
1524 int lmqi_dsec
; /* Last Member Query Interval */
1525 int lmqc
; /* Last Member Query Count */
1526 int lmqt_msec
; /* Last Member Query Time */
1528 group
= source
->source_group
;
1529 ifp
= group
->interface
;
1530 pim_ifp
= ifp
->info
;
1533 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1534 lmqc
= pim_ifp
->gm_last_member_query_count
;
1535 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1536 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1538 if (PIM_DEBUG_GM_TRACE
) {
1539 char group_str
[INET_ADDRSTRLEN
];
1540 char source_str
[INET_ADDRSTRLEN
];
1541 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1543 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1544 sizeof(source_str
));
1546 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1547 __func__
, group_str
, source_str
, ifname
, lmqc
,
1548 lmqi_dsec
, lmqt_msec
);
1551 igmp_source_timer_on(group
, source
, lmqt_msec
);
1554 void igmp_v3_send_query(struct gm_group
*group
, int fd
, const char *ifname
,
1555 char *query_buf
, int query_buf_size
, int num_sources
,
1556 struct in_addr dst_addr
, struct in_addr group_addr
,
1557 int query_max_response_time_dsec
, uint8_t s_flag
,
1558 uint8_t querier_robustness_variable
,
1559 uint16_t querier_query_interval
)
1562 uint8_t max_resp_code
;
1565 struct sockaddr_in to
;
1569 assert(num_sources
>= 0);
1571 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1572 if (msg_size
> query_buf_size
) {
1575 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1576 __FILE__
, __func__
, msg_size
, query_buf_size
);
1580 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1581 assert((s_flag
== 0) || (s_flag
== 1));
1583 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1584 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1587 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1589 If non-zero, the QRV field contains the [Robustness Variable]
1590 value used by the querier, i.e., the sender of the Query. If the
1591 querier's [Robustness Variable] exceeds 7, the maximum value of
1592 the QRV field, the QRV is set to zero.
1594 if (querier_robustness_variable
> 7) {
1595 querier_robustness_variable
= 0;
1598 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1599 query_buf
[1] = max_resp_code
;
1600 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1601 0; /* for computing checksum */
1602 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1604 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1605 query_buf
[9] = qqic
;
1606 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1609 checksum
= in_cksum(query_buf
, msg_size
);
1610 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1612 if (PIM_DEBUG_GM_PACKETS
) {
1613 char dst_str
[INET_ADDRSTRLEN
];
1614 char group_str
[INET_ADDRSTRLEN
];
1615 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1616 pim_inet4_dump("<group?>", group_addr
, group_str
,
1619 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1620 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1621 s_flag
, querier_robustness_variable
,
1622 querier_query_interval
, qqic
);
1625 memset(&to
, 0, sizeof(to
));
1626 to
.sin_family
= AF_INET
;
1627 to
.sin_addr
= dst_addr
;
1630 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1631 (struct sockaddr
*)&to
, tolen
);
1632 if (sent
!= (ssize_t
)msg_size
) {
1633 char dst_str
[INET_ADDRSTRLEN
];
1634 char group_str
[INET_ADDRSTRLEN
];
1635 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1636 pim_inet4_dump("<group?>", group_addr
, group_str
,
1640 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1641 dst_str
, ifname
, group_str
, msg_size
, errno
,
1642 safe_strerror(errno
));
1645 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1646 dst_str
, ifname
, group_str
, msg_size
, sent
);
1652 s_flag sanity test: s_flag must be set for general queries
1654 RFC 3376: 6.6.1. Timer Updates
1656 When a router sends or receives a query with a clear Suppress
1657 Router-Side Processing flag, it must update its timers to reflect
1658 the correct timeout values for the group or sources being queried.
1660 General queries don't trigger timer update.
1663 /* general query? */
1664 if (group_addr
.s_addr
== INADDR_ANY
) {
1665 char dst_str
[INET_ADDRSTRLEN
];
1666 char group_str
[INET_ADDRSTRLEN
];
1667 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1669 pim_inet4_dump("<group?>", group_addr
, group_str
,
1672 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1673 __func__
, dst_str
, ifname
, group_str
,
1679 void igmp_v3_recv_query(struct gm_sock
*igmp
, const char *from_str
,
1682 struct interface
*ifp
;
1683 struct pim_interface
*pim_ifp
;
1684 struct in_addr group_addr
;
1685 uint8_t resv_s_qrv
= 0;
1690 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1691 ifp
= igmp
->interface
;
1692 pim_ifp
= ifp
->info
;
1695 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1697 * Routers adopt the QRV value from the most recently received Query
1698 * as their own [Robustness Variable] value, unless that most
1699 * recently received QRV was zero, in which case the receivers use
1700 * the default [Robustness Variable] value specified in section 8.1
1701 * or a statically configured value.
1703 resv_s_qrv
= igmp_msg
[8];
1704 qrv
= 7 & resv_s_qrv
;
1705 igmp
->querier_robustness_variable
=
1706 qrv
? qrv
: pim_ifp
->gm_default_robustness_variable
;
1709 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1711 * Multicast routers that are not the current querier adopt the QQI
1712 * value from the most recently received Query as their own [Query
1713 * Interval] value, unless that most recently received QQI was zero,
1714 * in which case the receiving routers use the default.
1716 if (igmp
->t_other_querier_timer
) {
1717 /* other querier present */
1721 qqi
= igmp_msg_decode8to16(qqic
);
1722 igmp
->querier_query_interval
=
1723 qqi
? qqi
: pim_ifp
->gm_default_query_interval
;
1725 if (PIM_DEBUG_GM_TRACE
) {
1726 char ifaddr_str
[INET_ADDRSTRLEN
];
1727 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1728 sizeof(ifaddr_str
));
1730 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1732 qqi
? "recv-non-default" : "default",
1733 igmp
->querier_query_interval
, qqic
, from_str
);
1738 * RFC 3376: 6.6.1. Timer Updates
1740 * When a router sends or receives a query with a clear Suppress
1741 * Router-Side Processing flag, it must update its timers to reflect
1742 * the correct timeout values for the group or sources being queried.
1744 * General queries don't trigger timer update.
1746 s_flag
= (1 << 3) & resv_s_qrv
;
1749 /* s_flag is clear */
1751 if (group_addr
.s_addr
== INADDR_ANY
) {
1752 /* this is a general query */
1753 /* log that general query should have the s_flag set */
1755 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1756 from_str
, ifp
->name
);
1758 struct gm_group
*group
;
1760 /* this is a non-general query: perform timer updates */
1762 group
= find_group_by_addr(igmp
, group_addr
);
1764 int recv_num_sources
= ntohs(*(
1767 + IGMP_V3_NUMSOURCES_OFFSET
));
1770 * RFC 3376: 6.6.1. Timer Updates
1771 * Query Q(G,A): Source Timer for sources in A
1772 * are lowered to LMQT
1773 * Query Q(G): Group Timer is lowered to LMQT
1775 if (recv_num_sources
< 1) {
1776 /* Query Q(G): Group Timer is lowered to
1779 igmp_group_timer_lower_to_lmqt(group
);
1781 /* Query Q(G,A): Source Timer for
1782 * sources in A are lowered to LMQT */
1784 /* Scan sources in query and lower their
1786 struct in_addr
*sources
=
1789 + IGMP_V3_SOURCES_OFFSET
);
1790 for (i
= 0; i
< recv_num_sources
; ++i
) {
1791 struct in_addr src_addr
;
1792 struct gm_source
*src
;
1793 memcpy(&src_addr
, sources
+ i
,
1794 sizeof(struct in_addr
));
1795 src
= igmp_find_source_by_addr(
1798 igmp_source_timer_lower_to_lmqt(
1804 char group_str
[INET_ADDRSTRLEN
];
1805 pim_inet4_dump("<group?>", group_addr
,
1806 group_str
, sizeof(group_str
));
1808 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1809 from_str
, ifp
->name
, group_str
);
1812 } /* s_flag is clear: timer updates */
1815 static bool igmp_pkt_grp_addr_ok(struct interface
*ifp
, const char *from_str
,
1816 struct in_addr grp
, int rec_type
)
1818 struct pim_interface
*pim_ifp
;
1819 struct in_addr grp_addr
;
1821 pim_ifp
= ifp
->info
;
1823 /* determine filtering status for group */
1824 if (pim_is_group_filtered(pim_ifp
, &grp
)) {
1825 if (PIM_DEBUG_GM_PACKETS
) {
1827 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1828 &grp
.s_addr
, from_str
, ifp
->name
,
1829 pim_ifp
->boundary_oil_plist
);
1835 * If we receive a igmp report with the group in 224.0.0.0/24
1836 * then we should ignore it
1839 grp_addr
.s_addr
= ntohl(grp
.s_addr
);
1841 if (pim_is_group_224_0_0_0_24(grp_addr
)) {
1842 if (PIM_DEBUG_GM_PACKETS
) {
1844 "Ignoring IGMPv3 group record %pI4 from %s on %s group range falls in 224.0.0.0/24",
1845 &grp
.s_addr
, from_str
, ifp
->name
);
1853 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
1854 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
1857 if (pim_is_grp_ssm(pim_ifp
->pim
, grp
)) {
1859 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1860 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1861 if (PIM_DEBUG_GM_PACKETS
) {
1863 "Ignoring IGMPv3 group record %pI4 from %s on %s exclude mode in SSM range",
1864 &grp
.s_addr
, from_str
, ifp
->name
);
1873 int igmp_v3_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
1874 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1877 uint8_t *group_record
;
1878 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1879 struct interface
*ifp
= igmp
->interface
;
1880 struct pim_interface
*pim_ifp
= ifp
->info
;
1883 if (igmp
->mtrace_only
)
1886 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1888 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1889 from_str
, ifp
->name
, igmp_msg_len
,
1890 IGMP_V3_MSG_MIN_SIZE
);
1894 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
1896 "Recv IGMPv3 report from %s on %s with invalid checksum",
1897 from_str
, ifp
->name
);
1901 /* Collecting IGMP Rx stats */
1902 igmp
->igmp_stats
.report_v3
++;
1904 if (pim_ifp
->igmp_version
== 2) {
1906 "Received Version 3 packet but interface: %s is configured for version 2",
1912 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1913 if (num_groups
< 1) {
1915 "Recv IGMP report v3 from %s on %s: missing group records",
1916 from_str
, ifp
->name
);
1920 if (PIM_DEBUG_GM_PACKETS
) {
1922 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1923 from_str
, ifp
->name
, igmp_msg_len
, num_groups
);
1926 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1929 for (i
= 0; i
< num_groups
; ++i
) {
1930 struct in_addr rec_group
;
1935 int rec_num_sources
;
1938 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1941 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1942 from_str
, ifp
->name
);
1946 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1948 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1949 rec_num_sources
= ntohs(*(
1950 uint16_t *)(group_record
1951 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1954 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1955 sizeof(struct in_addr
));
1957 if (PIM_DEBUG_GM_PACKETS
) {
1959 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
1960 from_str
, ifp
->name
, i
, rec_type
,
1961 rec_auxdatalen
, rec_num_sources
,
1967 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1969 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1971 if ((src
+ 4) > report_pastend
) {
1973 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1974 from_str
, ifp
->name
);
1978 if (PIM_DEBUG_GM_PACKETS
) {
1981 if (!inet_ntop(AF_INET
, src
, src_str
,
1983 snprintf(src_str
, sizeof(src_str
),
1987 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
1988 from_str
, ifp
->name
, i
,
1989 &rec_group
, src_str
);
1991 } /* for (sources) */
1994 if (igmp_pkt_grp_addr_ok(ifp
, from_str
, rec_group
, rec_type
))
1996 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1997 igmpv3_report_isin(igmp
, from
, rec_group
,
1999 (struct in_addr
*)sources
);
2001 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
2003 igmp
, from
, rec_group
, rec_num_sources
,
2004 (struct in_addr
*)sources
, 0);
2006 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
2007 igmpv3_report_toin(igmp
, from
, rec_group
,
2009 (struct in_addr
*)sources
);
2011 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
2012 igmpv3_report_toex(igmp
, from
, rec_group
,
2014 (struct in_addr
*)sources
);
2016 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2017 igmpv3_report_allow(igmp
, from
, rec_group
,
2019 (struct in_addr
*)sources
);
2021 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2022 igmpv3_report_block(igmp
, from
, rec_group
,
2024 (struct in_addr
*)sources
);
2028 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2029 from_str
, ifp
->name
, rec_type
);
2033 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2035 } /* for (group records) */