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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "lib_errors.h"
27 #include "pim_iface.h"
29 #include "pim_igmpv3.h"
33 #include "pim_zebra.h"
36 static void group_retransmit_timer_on(struct igmp_group
*group
);
37 static long igmp_group_timer_remain_msec(struct igmp_group
*group
);
38 static long igmp_source_timer_remain_msec(struct igmp_source
*source
);
39 static void group_query_send(struct igmp_group
*group
);
40 static void source_query_send_by_flag(struct igmp_group
*group
,
41 int num_sources_tosend
);
43 static void on_trace(const char *label
, struct interface
*ifp
,
44 struct in_addr from
, struct in_addr group_addr
,
45 int num_sources
, struct in_addr
*sources
)
47 if (PIM_DEBUG_IGMP_TRACE
) {
48 char from_str
[INET_ADDRSTRLEN
];
49 char group_str
[INET_ADDRSTRLEN
];
51 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
52 pim_inet4_dump("<group?>", group_addr
, group_str
,
55 zlog_debug("%s: from %s on %s: group=%s sources=%d", label
,
56 from_str
, ifp
->name
, group_str
, num_sources
);
60 void igmp_group_reset_gmi(struct igmp_group
*group
)
62 long group_membership_interval_msec
;
63 struct pim_interface
*pim_ifp
;
64 struct igmp_sock
*igmp
;
65 struct interface
*ifp
;
67 igmp
= group
->group_igmp_sock
;
68 ifp
= igmp
->interface
;
72 RFC 3376: 8.4. Group Membership Interval
74 The Group Membership Interval is the amount of time that must pass
75 before a multicast router decides there are no more members of a
76 group or a particular source on a network.
78 This value MUST be ((the Robustness Variable) times (the Query
79 Interval)) plus (one Query Response Interval).
81 group_membership_interval_msec = querier_robustness_variable *
82 (1000 * querier_query_interval) +
83 100 * query_response_interval_dsec;
85 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
86 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
87 pim_ifp
->igmp_query_max_response_time_dsec
);
89 if (PIM_DEBUG_IGMP_TRACE
) {
90 char group_str
[INET_ADDRSTRLEN
];
91 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
94 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
95 group_str
, group_membership_interval_msec
/ 1000,
96 group_membership_interval_msec
% 1000, ifp
->name
);
100 RFC 3376: 6.2.2. Definition of Group Timers
102 The group timer is only used when a group is in EXCLUDE mode and
103 it represents the time for the *filter-mode* of the group to
104 expire and switch to INCLUDE mode.
106 zassert(group
->group_filtermode_isexcl
);
108 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
111 static int igmp_source_timer(struct thread
*t
)
113 struct igmp_source
*source
;
114 struct igmp_group
*group
;
116 source
= THREAD_ARG(t
);
118 group
= source
->source_group
;
120 if (PIM_DEBUG_IGMP_TRACE
) {
121 char group_str
[INET_ADDRSTRLEN
];
122 char source_str
[INET_ADDRSTRLEN
];
123 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
125 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
128 "%s: Source timer expired for group %s source %s on %s",
129 __PRETTY_FUNCTION__
, group_str
, source_str
,
130 group
->group_igmp_sock
->interface
->name
);
134 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
137 Filter-Mode Source Timer Value Action
138 ----------- ------------------ ------
139 INCLUDE TIMER == 0 Suggest to stop forwarding
140 traffic from source and
141 remove source record. If
142 there are no more source
143 records for the group, delete
146 EXCLUDE TIMER == 0 Suggest to not forward
148 (DO NOT remove record)
150 Source timer switched from (T > 0) to (T == 0): disable forwarding.
153 if (group
->group_filtermode_isexcl
) {
156 igmp_source_forward_stop(source
);
160 /* igmp_source_delete() will stop forwarding source */
161 igmp_source_delete(source
);
164 If there are no more source records for the group, delete
168 if (!listcount(group
->group_source_list
)) {
169 igmp_group_delete_empty_include(group
);
176 static void source_timer_off(struct igmp_group
*group
,
177 struct igmp_source
*source
)
179 if (!source
->t_source_timer
)
182 if (PIM_DEBUG_IGMP_TRACE
) {
183 char group_str
[INET_ADDRSTRLEN
];
184 char source_str
[INET_ADDRSTRLEN
];
185 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
187 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
190 "Cancelling TIMER event for group %s source %s on %s",
191 group_str
, source_str
,
192 group
->group_igmp_sock
->interface
->name
);
195 THREAD_OFF(source
->t_source_timer
);
198 static void igmp_source_timer_on(struct igmp_group
*group
,
199 struct igmp_source
*source
, long interval_msec
)
201 source_timer_off(group
, source
);
202 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
204 if (PIM_DEBUG_IGMP_EVENTS
) {
205 char group_str
[INET_ADDRSTRLEN
];
206 char source_str
[INET_ADDRSTRLEN
];
207 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
209 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
212 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
213 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
214 source_str
, group
->group_igmp_sock
->interface
->name
);
217 thread_add_timer_msec(master
, igmp_source_timer
, source
, interval_msec
,
218 &source
->t_source_timer
);
221 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
223 Source timer switched from (T == 0) to (T > 0): enable forwarding.
225 igmp_source_forward_start(pim_ifp
->pim
, source
);
228 void igmp_source_reset_gmi(struct igmp_sock
*igmp
, struct igmp_group
*group
,
229 struct igmp_source
*source
)
231 long group_membership_interval_msec
;
232 struct pim_interface
*pim_ifp
;
233 struct interface
*ifp
;
235 ifp
= igmp
->interface
;
238 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
239 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
240 pim_ifp
->igmp_query_max_response_time_dsec
);
242 if (PIM_DEBUG_IGMP_TRACE
) {
243 char group_str
[INET_ADDRSTRLEN
];
244 char source_str
[INET_ADDRSTRLEN
];
246 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
248 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
252 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
253 source_str
, group_membership_interval_msec
/ 1000,
254 group_membership_interval_msec
% 1000, group_str
,
258 igmp_source_timer_on(group
, source
, group_membership_interval_msec
);
261 static void source_mark_delete_flag(struct igmp_group
*group
)
263 struct listnode
*src_node
;
264 struct igmp_source
*src
;
266 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
267 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
271 static void source_mark_send_flag(struct igmp_group
*group
)
273 struct listnode
*src_node
;
274 struct igmp_source
*src
;
276 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
277 IGMP_SOURCE_DO_SEND(src
->source_flags
);
281 static int source_mark_send_flag_by_timer(struct igmp_group
*group
)
283 struct listnode
*src_node
;
284 struct igmp_source
*src
;
285 int num_marked_sources
= 0;
287 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
288 /* Is source timer running? */
289 if (src
->t_source_timer
) {
290 IGMP_SOURCE_DO_SEND(src
->source_flags
);
291 ++num_marked_sources
;
293 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
297 return num_marked_sources
;
300 static void source_clear_send_flag(struct list
*source_list
)
302 struct listnode
*src_node
;
303 struct igmp_source
*src
;
305 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
306 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
311 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
313 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
315 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
317 zassert(group
->group_filtermode_isexcl
);
319 if (listcount(group
->group_source_list
) < 1) {
320 igmp_anysource_forward_start(pim_ifp
->pim
, group
);
324 void igmp_source_free(struct igmp_source
*source
)
326 /* make sure there is no source timer running */
327 zassert(!source
->t_source_timer
);
329 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
332 static void source_channel_oil_detach(struct igmp_source
*source
)
334 if (source
->source_channel_oil
) {
335 pim_channel_oil_del(source
->source_channel_oil
);
336 source
->source_channel_oil
= NULL
;
341 igmp_source_delete: stop fowarding, and delete the source
342 igmp_source_forward_stop: stop fowarding, but keep the source
344 void igmp_source_delete(struct igmp_source
*source
)
346 struct igmp_group
*group
;
349 group
= source
->source_group
;
351 if (PIM_DEBUG_IGMP_TRACE
) {
352 char group_str
[INET_ADDRSTRLEN
];
353 char source_str
[INET_ADDRSTRLEN
];
354 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
356 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
359 "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
360 source_str
, group_str
, group
->group_igmp_sock
->fd
,
361 group
->group_igmp_sock
->interface
->name
,
362 source
->source_channel_oil
363 ? source
->source_channel_oil
->oil_ref_count
367 source_timer_off(group
, source
);
368 igmp_source_forward_stop(source
);
370 /* sanity check that forwarding has been disabled */
371 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
372 char group_str
[INET_ADDRSTRLEN
];
373 char source_str
[INET_ADDRSTRLEN
];
374 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
376 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
379 "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
380 __PRETTY_FUNCTION__
, source_str
, group_str
,
381 group
->group_igmp_sock
->fd
,
382 group
->group_igmp_sock
->interface
->name
);
386 source_channel_oil_detach(source
);
389 notice that listnode_delete() can't be moved
390 into igmp_source_free() because the later is
391 called by list_delete_all_node()
393 listnode_delete(group
->group_source_list
, source
);
395 src
.s_addr
= source
->source_addr
.s_addr
;
396 igmp_source_free(source
);
398 /* Group source list is empty and current source is * then
399 *,G group going away so do not trigger start */
400 if (group
->group_filtermode_isexcl
401 && (listcount(group
->group_source_list
) != 0)
402 && src
.s_addr
!= INADDR_ANY
) {
403 group_exclude_fwd_anysrc_ifempty(group
);
407 static void source_delete_by_flag(struct list
*source_list
)
409 struct listnode
*src_node
;
410 struct listnode
*src_nextnode
;
411 struct igmp_source
*src
;
413 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
414 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
415 igmp_source_delete(src
);
418 void igmp_source_delete_expired(struct list
*source_list
)
420 struct listnode
*src_node
;
421 struct listnode
*src_nextnode
;
422 struct igmp_source
*src
;
424 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
425 if (!src
->t_source_timer
)
426 igmp_source_delete(src
);
429 struct igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
430 struct in_addr src_addr
)
432 struct listnode
*src_node
;
433 struct igmp_source
*src
;
435 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
436 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
442 struct igmp_source
*source_new(struct igmp_group
*group
,
443 struct in_addr src_addr
)
445 struct igmp_source
*src
;
447 if (PIM_DEBUG_IGMP_TRACE
) {
448 char group_str
[INET_ADDRSTRLEN
];
449 char source_str
[INET_ADDRSTRLEN
];
450 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
452 pim_inet4_dump("<source?>", src_addr
, source_str
,
455 "Creating new IGMP source %s for group %s on socket %d interface %s",
456 source_str
, group_str
, group
->group_igmp_sock
->fd
,
457 group
->group_igmp_sock
->interface
->name
);
460 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
462 src
->t_source_timer
= NULL
;
463 src
->source_group
= group
; /* back pointer */
464 src
->source_addr
= src_addr
;
465 src
->source_creation
= pim_time_monotonic_sec();
466 src
->source_flags
= 0;
467 src
->source_query_retransmit_count
= 0;
468 src
->source_channel_oil
= NULL
;
470 listnode_add(group
->group_source_list
, src
);
472 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
473 igmp_anysource_forward_stop(group
);
478 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
479 struct igmp_group
*group
,
480 struct in_addr src_addr
)
482 struct igmp_source
*src
;
484 src
= igmp_find_source_by_addr(group
, src_addr
);
489 src
= source_new(group
, src_addr
);
494 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
495 struct in_addr group_addr
, int num_sources
,
496 struct in_addr
*sources
)
498 struct igmp_source
*source
;
499 struct igmp_group
*group
;
502 /* non-existant group is created as INCLUDE {empty} */
503 group
= igmp_add_group_by_addr(igmp
, group_addr
);
508 /* scan received sources */
509 for (i
= 0; i
< num_sources
; ++i
) {
510 struct in_addr
*src_addr
;
512 src_addr
= sources
+ i
;
514 source
= add_source_by_addr(igmp
, group
, *src_addr
);
520 RFC 3376: 6.4.1. Reception of Current-State Records
522 When receiving IS_IN reports for groups in EXCLUDE mode is
523 sources should be moved from set with (timers = 0) to set with
526 igmp_source_reset_gmi() below, resetting the source timers to
527 GMI, accomplishes this.
529 igmp_source_reset_gmi(igmp
, group
, source
);
531 } /* scan received sources */
533 if ((num_sources
== 0) && (group
->group_filtermode_isexcl
)
534 && (listcount(group
->group_source_list
) == 1)) {
535 struct in_addr star
= {.s_addr
= INADDR_ANY
};
537 source
= igmp_find_source_by_addr(group
, star
);
539 igmp_source_reset_gmi(igmp
, group
, source
);
543 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
544 struct in_addr group_addr
, int num_sources
,
545 struct in_addr
*sources
)
547 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
548 num_sources
, sources
);
550 allow(igmp
, from
, group_addr
, num_sources
, sources
);
553 static void isex_excl(struct igmp_group
*group
, int num_sources
,
554 struct in_addr
*sources
)
556 struct igmp_source
*source
;
560 zassert(group
->group_filtermode_isexcl
);
562 /* E.1: set deletion flag for known sources (X,Y) */
563 source_mark_delete_flag(group
);
565 /* scan received sources (A) */
566 for (i
= 0; i
< num_sources
; ++i
) {
567 struct in_addr
*src_addr
;
569 src_addr
= sources
+ i
;
571 /* E.2: lookup reported source from (A) in (X,Y) */
572 source
= igmp_find_source_by_addr(group
, *src_addr
);
574 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
575 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
577 /* E.4: if not found, create source with timer=GMI:
579 source
= source_new(group
, *src_addr
);
580 zassert(!source
->t_source_timer
); /* timer == 0 */
581 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
583 zassert(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
->group_igmp_sock
, group
,
603 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
604 source_delete_by_flag(group
->group_source_list
);
607 static void isex_incl(struct igmp_group
*group
, int num_sources
,
608 struct in_addr
*sources
)
613 zassert(!group
->group_filtermode_isexcl
);
615 /* I.1: set deletion flag for known sources (A) */
616 source_mark_delete_flag(group
);
618 /* scan received sources (B) */
619 for (i
= 0; i
< num_sources
; ++i
) {
620 struct igmp_source
*source
;
621 struct in_addr
*src_addr
;
623 src_addr
= sources
+ i
;
625 /* I.2: lookup reported source (B) */
626 source
= igmp_find_source_by_addr(group
, *src_addr
);
628 /* I.3: if found, clear deletion flag (A*B) */
629 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
631 /* I.4: if not found, create source with timer=0 (B-A)
633 source
= source_new(group
, *src_addr
);
634 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
637 } /* scan received sources */
639 /* I.5: delete all sources marked with deletion flag (A-B) */
640 source_delete_by_flag(group
->group_source_list
);
642 group
->group_filtermode_isexcl
= 1; /* boolean=true */
644 zassert(group
->group_filtermode_isexcl
);
646 group_exclude_fwd_anysrc_ifempty(group
);
649 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
650 struct in_addr group_addr
, int num_sources
,
651 struct in_addr
*sources
, int from_igmp_v2_report
)
653 struct interface
*ifp
= igmp
->interface
;
654 struct igmp_group
*group
;
656 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
659 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
662 /* non-existant group is created as INCLUDE {empty} */
663 group
= igmp_add_group_by_addr(igmp
, group_addr
);
668 /* So we can display how we learned the group in our show command output
670 if (from_igmp_v2_report
)
671 group
->igmp_version
= 2;
673 if (group
->group_filtermode_isexcl
) {
675 isex_excl(group
, num_sources
, sources
);
678 isex_incl(group
, num_sources
, sources
);
679 zassert(group
->group_filtermode_isexcl
);
682 zassert(group
->group_filtermode_isexcl
);
684 igmp_group_reset_gmi(group
);
687 static void toin_incl(struct igmp_group
*group
, int num_sources
,
688 struct in_addr
*sources
)
690 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
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 igmp_source
*source
;
700 struct in_addr
*src_addr
;
702 src_addr
= sources
+ i
;
704 /* Lookup reported source (B) */
705 source
= igmp_find_source_by_addr(group
, *src_addr
);
707 /* If found, clear SEND flag (A*B) */
708 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
709 --num_sources_tosend
;
711 /* If not found, create new source */
712 source
= source_new(group
, *src_addr
);
716 igmp_source_reset_gmi(igmp
, group
, source
);
719 /* Send sources marked with SEND flag: Q(G,A-B) */
720 if (num_sources_tosend
> 0) {
721 source_query_send_by_flag(group
, num_sources_tosend
);
725 static void toin_excl(struct igmp_group
*group
, int num_sources
,
726 struct in_addr
*sources
)
728 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
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 igmp_source
*source
;
738 struct in_addr
*src_addr
;
740 src_addr
= sources
+ i
;
742 /* Lookup reported source (A) */
743 source
= igmp_find_source_by_addr(group
, *src_addr
);
745 if (source
->t_source_timer
) {
746 /* If found and timer running, clear SEND flag
748 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
749 --num_sources_tosend
;
752 /* If not found, create new source */
753 source
= source_new(group
, *src_addr
);
757 igmp_source_reset_gmi(igmp
, group
, source
);
760 /* Send sources marked with SEND flag: Q(G,X-A) */
761 if (num_sources_tosend
> 0) {
762 source_query_send_by_flag(group
, num_sources_tosend
);
766 group_query_send(group
);
769 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
770 struct in_addr group_addr
, int num_sources
,
771 struct in_addr
*sources
)
773 struct interface
*ifp
= igmp
->interface
;
774 struct igmp_group
*group
;
776 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
780 * If the requested filter mode is INCLUDE *and* the requested source
781 * list is empty, then the entry corresponding to the requested
782 * interface and multicast address is deleted if present. If no such
783 * entry is present, the request is ignored.
786 /* non-existant group is created as INCLUDE {empty} */
787 group
= igmp_add_group_by_addr(igmp
, group_addr
);
792 group
= find_group_by_addr(igmp
, group_addr
);
797 if (group
->group_filtermode_isexcl
) {
799 toin_excl(group
, num_sources
, sources
);
802 toin_incl(group
, num_sources
, sources
);
806 static void toex_incl(struct igmp_group
*group
, int num_sources
,
807 struct in_addr
*sources
)
809 int num_sources_tosend
= 0;
812 zassert(!group
->group_filtermode_isexcl
);
814 /* Set DELETE flag for all known sources (A) */
815 source_mark_delete_flag(group
);
817 /* Clear off SEND flag from all known sources (A) */
818 source_clear_send_flag(group
->group_source_list
);
820 /* Scan received sources (B) */
821 for (i
= 0; i
< num_sources
; ++i
) {
822 struct igmp_source
*source
;
823 struct in_addr
*src_addr
;
825 src_addr
= sources
+ i
;
827 /* Lookup reported source (B) */
828 source
= igmp_find_source_by_addr(group
, *src_addr
);
830 /* If found, clear deletion flag: (A*B) */
831 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
832 /* and set SEND flag (A*B) */
833 IGMP_SOURCE_DO_SEND(source
->source_flags
);
834 ++num_sources_tosend
;
836 /* If source not found, create source with timer=0:
838 source
= source_new(group
, *src_addr
);
839 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
842 } /* Scan received sources (B) */
844 group
->group_filtermode_isexcl
= 1; /* boolean=true */
846 /* Delete all sources marked with DELETE flag (A-B) */
847 source_delete_by_flag(group
->group_source_list
);
849 /* Send sources marked with SEND flag: Q(G,A*B) */
850 if (num_sources_tosend
> 0) {
851 source_query_send_by_flag(group
, num_sources_tosend
);
854 zassert(group
->group_filtermode_isexcl
);
856 group_exclude_fwd_anysrc_ifempty(group
);
859 static void toex_excl(struct igmp_group
*group
, int num_sources
,
860 struct in_addr
*sources
)
862 int num_sources_tosend
= 0;
865 /* set DELETE flag for all known sources (X,Y) */
866 source_mark_delete_flag(group
);
868 /* clear off SEND flag from all known sources (X,Y) */
869 source_clear_send_flag(group
->group_source_list
);
871 if (num_sources
== 0) {
872 struct igmp_source
*source
;
873 struct in_addr any
= {.s_addr
= INADDR_ANY
};
875 source
= igmp_find_source_by_addr(group
, any
);
877 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
880 /* scan received sources (A) */
881 for (i
= 0; i
< num_sources
; ++i
) {
882 struct igmp_source
*source
;
883 struct in_addr
*src_addr
;
885 src_addr
= sources
+ i
;
887 /* lookup reported source (A) in known sources (X,Y) */
888 source
= igmp_find_source_by_addr(group
, *src_addr
);
890 /* if found, clear off DELETE flag from reported source
892 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
894 /* if not found, create source with Group Timer:
895 * (A-X-Y)=Group Timer */
896 long group_timer_msec
;
897 source
= source_new(group
, *src_addr
);
899 zassert(!source
->t_source_timer
); /* timer == 0 */
900 group_timer_msec
= igmp_group_timer_remain_msec(group
);
901 igmp_source_timer_on(group
, source
, group_timer_msec
);
902 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
904 /* make sure source is created with DELETE flag unset */
905 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
908 /* make sure reported source has DELETE flag unset */
909 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
911 if (source
->t_source_timer
) {
912 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
913 IGMP_SOURCE_DO_SEND(source
->source_flags
);
914 ++num_sources_tosend
;
917 } /* scan received sources (A) */
920 delete all sources marked with DELETE flag:
924 source_delete_by_flag(group
->group_source_list
);
926 /* send sources marked with SEND flag: Q(G,A-Y) */
927 if (num_sources_tosend
> 0) {
928 source_query_send_by_flag(group
, num_sources_tosend
);
932 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
933 struct in_addr group_addr
, int num_sources
,
934 struct in_addr
*sources
)
936 struct interface
*ifp
= igmp
->interface
;
937 struct igmp_group
*group
;
939 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
942 /* non-existant group is created as INCLUDE {empty} */
943 group
= igmp_add_group_by_addr(igmp
, group_addr
);
948 if (group
->group_filtermode_isexcl
) {
950 toex_excl(group
, num_sources
, sources
);
953 toex_incl(group
, num_sources
, sources
);
954 zassert(group
->group_filtermode_isexcl
);
956 zassert(group
->group_filtermode_isexcl
);
958 /* Group Timer=GMI */
959 igmp_group_reset_gmi(group
);
962 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
963 struct in_addr group_addr
, int num_sources
,
964 struct in_addr
*sources
)
966 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
967 num_sources
, sources
);
969 allow(igmp
, from
, group_addr
, num_sources
, sources
);
973 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
975 When transmitting a group specific query, if the group timer is
976 larger than LMQT, the "Suppress Router-Side Processing" bit is set
977 in the query message.
979 static void group_retransmit_group(struct igmp_group
*group
)
981 struct igmp_sock
*igmp
;
982 struct pim_interface
*pim_ifp
;
983 long lmqc
; /* Last Member Query Count */
984 long lmqi_msec
; /* Last Member Query Interval */
985 long lmqt_msec
; /* Last Member Query Time */
989 igmp
= group
->group_igmp_sock
;
990 pim_ifp
= igmp
->interface
->info
;
992 if (pim_ifp
->igmp_version
== 3) {
993 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
995 query_buf_size
= IGMP_V12_MSG_SIZE
;
998 char query_buf
[query_buf_size
];
1000 lmqc
= igmp
->querier_robustness_variable
;
1001 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1002 lmqt_msec
= lmqc
* lmqi_msec
;
1005 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1007 When transmitting a group specific query, if the group timer is
1008 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1009 in the query message.
1011 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1013 if (PIM_DEBUG_IGMP_TRACE
) {
1014 char group_str
[INET_ADDRSTRLEN
];
1015 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1018 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1019 group_str
, igmp
->interface
->name
, s_flag
,
1020 group
->group_specific_query_retransmit_count
);
1024 RFC3376: 4.1.12. IP Destination Addresses for Queries
1026 Group-Specific and Group-and-Source-Specific Queries are sent with
1027 an IP destination address equal to the multicast address of
1031 igmp_send_query(pim_ifp
->igmp_version
, group
, igmp
->fd
,
1032 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
1033 0 /* num_sources_tosend */,
1034 group
->group_addr
/* dst_addr */,
1035 group
->group_addr
/* group_addr */,
1036 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1037 s_flag
, igmp
->querier_robustness_variable
,
1038 igmp
->querier_query_interval
);
1042 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1044 When building a group and source specific query for a group G, two
1045 separate query messages are sent for the group. The first one has
1046 the "Suppress Router-Side Processing" bit set and contains all the
1047 sources with retransmission state and timers greater than LMQT. The
1048 second has the "Suppress Router-Side Processing" bit clear and
1049 contains all the sources with retransmission state and timers lower
1050 or equal to LMQT. If either of the two calculated messages does not
1051 contain any sources, then its transmission is suppressed.
1053 static int group_retransmit_sources(struct igmp_group
*group
,
1054 int send_with_sflag_set
)
1056 struct igmp_sock
*igmp
;
1057 struct pim_interface
*pim_ifp
;
1058 long lmqc
; /* Last Member Query Count */
1059 long lmqi_msec
; /* Last Member Query Interval */
1060 long lmqt_msec
; /* Last Member Query Time */
1061 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1062 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1063 int query_buf1_max_sources
;
1064 int query_buf2_max_sources
;
1065 struct in_addr
*source_addr1
;
1066 struct in_addr
*source_addr2
;
1067 int num_sources_tosend1
;
1068 int num_sources_tosend2
;
1069 struct listnode
*src_node
;
1070 struct igmp_source
*src
;
1071 int num_retransmit_sources_left
= 0;
1073 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1074 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1076 igmp
= group
->group_igmp_sock
;
1077 pim_ifp
= igmp
->interface
->info
;
1079 lmqc
= igmp
->querier_robustness_variable
;
1080 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1081 lmqt_msec
= lmqc
* lmqi_msec
;
1083 /* Scan all group sources */
1084 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1086 /* Source has retransmission state? */
1087 if (src
->source_query_retransmit_count
< 1)
1090 if (--src
->source_query_retransmit_count
> 0) {
1091 ++num_retransmit_sources_left
;
1094 /* Copy source address into appropriate query buffer */
1095 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1096 *source_addr1
= src
->source_addr
;
1099 *source_addr2
= src
->source_addr
;
1104 num_sources_tosend1
=
1106 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1107 num_sources_tosend2
=
1109 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1111 if (PIM_DEBUG_IGMP_TRACE
) {
1112 char group_str
[INET_ADDRSTRLEN
];
1113 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1116 "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",
1117 group_str
, igmp
->interface
->name
, num_sources_tosend1
,
1118 num_sources_tosend2
, send_with_sflag_set
,
1119 num_retransmit_sources_left
);
1122 if (num_sources_tosend1
> 0) {
1124 Send group-and-source-specific query with s_flag set and all
1125 sources with timers greater than LMQT.
1128 if (send_with_sflag_set
) {
1130 query_buf1_max_sources
=
1131 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1133 if (num_sources_tosend1
> query_buf1_max_sources
) {
1134 char group_str
[INET_ADDRSTRLEN
];
1135 pim_inet4_dump("<group?>", group
->group_addr
,
1136 group_str
, sizeof(group_str
));
1138 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1139 __PRETTY_FUNCTION__
, group_str
,
1140 igmp
->interface
->name
,
1141 num_sources_tosend1
, sizeof(query_buf1
),
1142 query_buf1_max_sources
);
1145 RFC3376: 4.1.12. IP Destination Addresses for
1148 Group-Specific and Group-and-Source-Specific
1149 Queries are sent with
1150 an IP destination address equal to the
1151 multicast address of
1156 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1157 igmp
->interface
->name
, query_buf1
,
1158 sizeof(query_buf1
), num_sources_tosend1
,
1159 group
->group_addr
, group
->group_addr
,
1160 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1162 igmp
->querier_robustness_variable
,
1163 igmp
->querier_query_interval
);
1166 } /* send_with_sflag_set */
1169 if (num_sources_tosend2
> 0) {
1171 Send group-and-source-specific query with s_flag clear and all
1172 sources with timers lower or equal to LMQT.
1175 query_buf2_max_sources
=
1176 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1177 if (num_sources_tosend2
> query_buf2_max_sources
) {
1178 char group_str
[INET_ADDRSTRLEN
];
1179 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1182 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1183 __PRETTY_FUNCTION__
, group_str
,
1184 igmp
->interface
->name
, num_sources_tosend2
,
1185 sizeof(query_buf2
), query_buf2_max_sources
);
1188 RFC3376: 4.1.12. IP Destination Addresses for Queries
1190 Group-Specific and Group-and-Source-Specific Queries
1192 an IP destination address equal to the multicast
1198 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1199 igmp
->interface
->name
, query_buf2
,
1200 sizeof(query_buf2
), num_sources_tosend2
,
1201 group
->group_addr
, group
->group_addr
,
1202 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1204 igmp
->querier_robustness_variable
,
1205 igmp
->querier_query_interval
);
1209 return num_retransmit_sources_left
;
1212 static int igmp_group_retransmit(struct thread
*t
)
1214 struct igmp_group
*group
;
1215 int num_retransmit_sources_left
;
1216 int send_with_sflag_set
; /* boolean */
1218 group
= THREAD_ARG(t
);
1220 if (PIM_DEBUG_IGMP_TRACE
) {
1221 char group_str
[INET_ADDRSTRLEN
];
1222 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1224 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1225 group
->group_igmp_sock
->interface
->name
);
1228 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1229 if (group
->group_specific_query_retransmit_count
> 0) {
1231 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1232 group_retransmit_group(group
);
1233 --group
->group_specific_query_retransmit_count
;
1237 If a group specific query is scheduled to be transmitted at
1239 same time as a group and source specific query for the same
1241 then transmission of the group and source specific message
1243 "Suppress Router-Side Processing" bit set may be suppressed.
1245 send_with_sflag_set
= 0; /* boolean=false */
1247 send_with_sflag_set
= 1; /* boolean=true */
1250 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1251 num_retransmit_sources_left
=
1252 group_retransmit_sources(group
, send_with_sflag_set
);
1255 Keep group retransmit timer running if there is any retransmit
1258 if ((num_retransmit_sources_left
> 0)
1259 || (group
->group_specific_query_retransmit_count
> 0)) {
1260 group_retransmit_timer_on(group
);
1267 group_retransmit_timer_on:
1268 if group retransmit timer isn't running, starts it;
1269 otherwise, do nothing
1271 static void group_retransmit_timer_on(struct igmp_group
*group
)
1273 struct igmp_sock
*igmp
;
1274 struct pim_interface
*pim_ifp
;
1275 long lmqi_msec
; /* Last Member Query Interval */
1277 /* if group retransmit timer is running, do nothing */
1278 if (group
->t_group_query_retransmit_timer
) {
1282 igmp
= group
->group_igmp_sock
;
1283 pim_ifp
= igmp
->interface
->info
;
1285 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1287 if (PIM_DEBUG_IGMP_TRACE
) {
1288 char group_str
[INET_ADDRSTRLEN
];
1289 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1292 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1293 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1294 igmp
->interface
->name
);
1297 thread_add_timer_msec(master
, igmp_group_retransmit
, group
, lmqi_msec
,
1298 &group
->t_group_query_retransmit_timer
);
1301 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1303 return pim_time_timer_remain_msec(group
->t_group_timer
);
1306 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1308 return pim_time_timer_remain_msec(source
->t_source_timer
);
1312 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1314 static void group_query_send(struct igmp_group
*group
)
1316 long lmqc
; /* Last Member Query Count */
1318 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1320 /* lower group timer to lmqt */
1321 igmp_group_timer_lower_to_lmqt(group
);
1323 /* reset retransmission counter */
1324 group
->group_specific_query_retransmit_count
= lmqc
;
1326 /* immediately send group specific query (decrease retransmit counter by
1328 group_retransmit_group(group
);
1330 /* make sure group retransmit timer is running */
1331 group_retransmit_timer_on(group
);
1335 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1337 static void source_query_send_by_flag(struct igmp_group
*group
,
1338 int num_sources_tosend
)
1340 struct igmp_sock
*igmp
;
1341 struct pim_interface
*pim_ifp
;
1342 struct listnode
*src_node
;
1343 struct igmp_source
*src
;
1344 long lmqc
; /* Last Member Query Count */
1345 long lmqi_msec
; /* Last Member Query Interval */
1346 long lmqt_msec
; /* Last Member Query Time */
1348 zassert(num_sources_tosend
> 0);
1350 igmp
= group
->group_igmp_sock
;
1351 pim_ifp
= igmp
->interface
->info
;
1353 lmqc
= igmp
->querier_robustness_variable
;
1354 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1355 lmqt_msec
= lmqc
* lmqi_msec
;
1358 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1361 (...) for each of the sources in X of group G, with source timer
1364 o Set number of retransmissions for each source to [Last Member
1366 o Lower source timer to LMQT.
1368 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1369 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1370 /* source "src" in X of group G */
1371 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1372 src
->source_query_retransmit_count
= lmqc
;
1373 igmp_source_timer_lower_to_lmqt(src
);
1378 /* send group-and-source specific queries */
1379 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1381 /* make sure group retransmit timer is running */
1382 group_retransmit_timer_on(group
);
1385 static void block_excl(struct igmp_group
*group
, int num_sources
,
1386 struct in_addr
*sources
)
1388 int num_sources_tosend
= 0;
1391 /* 1. clear off SEND flag from all known sources (X,Y) */
1392 source_clear_send_flag(group
->group_source_list
);
1394 /* 2. scan received sources (A) */
1395 for (i
= 0; i
< num_sources
; ++i
) {
1396 struct igmp_source
*source
;
1397 struct in_addr
*src_addr
;
1399 src_addr
= sources
+ i
;
1401 /* lookup reported source (A) in known sources (X,Y) */
1402 source
= igmp_find_source_by_addr(group
, *src_addr
);
1404 /* 3: if not found, create source with Group Timer:
1405 * (A-X-Y)=Group Timer */
1406 long group_timer_msec
;
1407 source
= source_new(group
, *src_addr
);
1409 zassert(!source
->t_source_timer
); /* timer == 0 */
1410 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1411 igmp_source_timer_on(group
, source
, group_timer_msec
);
1412 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1415 if (source
->t_source_timer
) {
1416 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1417 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1418 ++num_sources_tosend
;
1422 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1423 if (num_sources_tosend
> 0) {
1424 source_query_send_by_flag(group
, num_sources_tosend
);
1428 static void block_incl(struct igmp_group
*group
, int num_sources
,
1429 struct in_addr
*sources
)
1431 int num_sources_tosend
= 0;
1434 /* 1. clear off SEND flag from all known sources (B) */
1435 source_clear_send_flag(group
->group_source_list
);
1437 /* 2. scan received sources (A) */
1438 for (i
= 0; i
< num_sources
; ++i
) {
1439 struct igmp_source
*source
;
1440 struct in_addr
*src_addr
;
1442 src_addr
= sources
+ i
;
1444 /* lookup reported source (A) in known sources (B) */
1445 source
= igmp_find_source_by_addr(group
, *src_addr
);
1447 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1448 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1449 ++num_sources_tosend
;
1453 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1454 if (num_sources_tosend
> 0) {
1455 source_query_send_by_flag(group
, num_sources_tosend
);
1459 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1460 struct in_addr group_addr
, int num_sources
,
1461 struct in_addr
*sources
)
1463 struct interface
*ifp
= igmp
->interface
;
1464 struct igmp_group
*group
;
1466 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
1469 /* non-existant group is created as INCLUDE {empty} */
1470 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1475 if (group
->group_filtermode_isexcl
) {
1477 block_excl(group
, num_sources
, sources
);
1480 block_incl(group
, num_sources
, sources
);
1484 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1486 struct igmp_sock
*igmp
;
1487 struct interface
*ifp
;
1488 struct pim_interface
*pim_ifp
;
1490 int lmqi_dsec
; /* Last Member Query Interval */
1491 int lmqc
; /* Last Member Query Count */
1492 int lmqt_msec
; /* Last Member Query Time */
1495 RFC 3376: 6.2.2. Definition of Group Timers
1497 The group timer is only used when a group is in EXCLUDE mode and
1498 it represents the time for the *filter-mode* of the group to
1499 expire and switch to INCLUDE mode.
1501 if (!group
->group_filtermode_isexcl
) {
1505 igmp
= group
->group_igmp_sock
;
1506 ifp
= igmp
->interface
;
1507 pim_ifp
= ifp
->info
;
1510 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1511 lmqc
= igmp
->querier_robustness_variable
;
1512 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1513 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1515 if (PIM_DEBUG_IGMP_TRACE
) {
1516 char group_str
[INET_ADDRSTRLEN
];
1517 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1520 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1521 __PRETTY_FUNCTION__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1525 zassert(group
->group_filtermode_isexcl
);
1527 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1530 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1532 struct igmp_group
*group
;
1533 struct igmp_sock
*igmp
;
1534 struct interface
*ifp
;
1535 struct pim_interface
*pim_ifp
;
1537 int lmqi_dsec
; /* Last Member Query Interval */
1538 int lmqc
; /* Last Member Query Count */
1539 int lmqt_msec
; /* Last Member Query Time */
1541 group
= source
->source_group
;
1542 igmp
= group
->group_igmp_sock
;
1543 ifp
= igmp
->interface
;
1544 pim_ifp
= ifp
->info
;
1547 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1548 lmqc
= igmp
->querier_robustness_variable
;
1549 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1550 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1552 if (PIM_DEBUG_IGMP_TRACE
) {
1553 char group_str
[INET_ADDRSTRLEN
];
1554 char source_str
[INET_ADDRSTRLEN
];
1555 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1557 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1558 sizeof(source_str
));
1560 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1561 __PRETTY_FUNCTION__
, group_str
, source_str
, ifname
,
1562 lmqc
, lmqi_dsec
, lmqt_msec
);
1565 igmp_source_timer_on(group
, source
, lmqt_msec
);
1568 void igmp_v3_send_query(struct igmp_group
*group
, int fd
, const char *ifname
,
1569 char *query_buf
, int query_buf_size
, int num_sources
,
1570 struct in_addr dst_addr
, struct in_addr group_addr
,
1571 int query_max_response_time_dsec
, uint8_t s_flag
,
1572 uint8_t querier_robustness_variable
,
1573 uint16_t querier_query_interval
)
1576 uint8_t max_resp_code
;
1579 struct sockaddr_in to
;
1583 zassert(num_sources
>= 0);
1585 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1586 if (msg_size
> query_buf_size
) {
1588 LIB_ERR_DEVELOPMENT
,
1589 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1590 __FILE__
, __PRETTY_FUNCTION__
, msg_size
,
1595 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1596 zassert((s_flag
== 0) || (s_flag
== 1));
1598 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1599 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1602 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1604 If non-zero, the QRV field contains the [Robustness Variable]
1605 value used by the querier, i.e., the sender of the Query. If the
1606 querier's [Robustness Variable] exceeds 7, the maximum value of
1607 the QRV field, the QRV is set to zero.
1609 if (querier_robustness_variable
> 7) {
1610 querier_robustness_variable
= 0;
1613 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1614 query_buf
[1] = max_resp_code
;
1615 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1616 0; /* for computing checksum */
1617 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1619 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1620 query_buf
[9] = qqic
;
1621 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1624 checksum
= in_cksum(query_buf
, msg_size
);
1625 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1627 if (PIM_DEBUG_IGMP_PACKETS
) {
1628 char dst_str
[INET_ADDRSTRLEN
];
1629 char group_str
[INET_ADDRSTRLEN
];
1630 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1631 pim_inet4_dump("<group?>", group_addr
, group_str
,
1634 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1635 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1636 s_flag
, querier_robustness_variable
,
1637 querier_query_interval
, qqic
);
1640 memset(&to
, 0, sizeof(to
));
1641 to
.sin_family
= AF_INET
;
1642 to
.sin_addr
= dst_addr
;
1645 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1646 (struct sockaddr
*)&to
, tolen
);
1647 if (sent
!= (ssize_t
)msg_size
) {
1648 char dst_str
[INET_ADDRSTRLEN
];
1649 char group_str
[INET_ADDRSTRLEN
];
1650 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1651 pim_inet4_dump("<group?>", group_addr
, group_str
,
1655 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1656 dst_str
, ifname
, group_str
, msg_size
, errno
,
1657 safe_strerror(errno
));
1660 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1661 dst_str
, ifname
, group_str
, msg_size
, sent
);
1667 s_flag sanity test: s_flag must be set for general queries
1669 RFC 3376: 6.6.1. Timer Updates
1671 When a router sends or receives a query with a clear Suppress
1672 Router-Side Processing flag, it must update its timers to reflect
1673 the correct timeout values for the group or sources being queried.
1675 General queries don't trigger timer update.
1678 /* general query? */
1679 if (PIM_INADDR_IS_ANY(group_addr
)) {
1680 char dst_str
[INET_ADDRSTRLEN
];
1681 char group_str
[INET_ADDRSTRLEN
];
1682 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1684 pim_inet4_dump("<group?>", group_addr
, group_str
,
1687 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1688 __PRETTY_FUNCTION__
, dst_str
, ifname
, group_str
,
1694 void igmp_v3_recv_query(struct igmp_sock
*igmp
, const char *from_str
,
1697 struct interface
*ifp
;
1698 struct pim_interface
*pim_ifp
;
1699 struct in_addr group_addr
;
1700 uint8_t resv_s_qrv
= 0;
1705 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1706 ifp
= igmp
->interface
;
1707 pim_ifp
= ifp
->info
;
1710 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1712 * Routers adopt the QRV value from the most recently received Query
1713 * as their own [Robustness Variable] value, unless that most
1714 * recently received QRV was zero, in which case the receivers use
1715 * the default [Robustness Variable] value specified in section 8.1
1716 * or a statically configured value.
1718 resv_s_qrv
= igmp_msg
[8];
1719 qrv
= 7 & resv_s_qrv
;
1720 igmp
->querier_robustness_variable
=
1721 qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1724 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1726 * Multicast routers that are not the current querier adopt the QQI
1727 * value from the most recently received Query as their own [Query
1728 * Interval] value, unless that most recently received QQI was zero,
1729 * in which case the receiving routers use the default.
1731 if (igmp
->t_other_querier_timer
) {
1732 /* other querier present */
1736 qqi
= igmp_msg_decode8to16(qqic
);
1737 igmp
->querier_query_interval
=
1738 qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1740 if (PIM_DEBUG_IGMP_TRACE
) {
1741 char ifaddr_str
[INET_ADDRSTRLEN
];
1742 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1743 sizeof(ifaddr_str
));
1745 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1747 qqi
? "recv-non-default" : "default",
1748 igmp
->querier_query_interval
, qqic
, from_str
);
1753 * RFC 3376: 6.6.1. Timer Updates
1755 * When a router sends or receives a query with a clear Suppress
1756 * Router-Side Processing flag, it must update its timers to reflect
1757 * the correct timeout values for the group or sources being queried.
1759 * General queries don't trigger timer update.
1761 s_flag
= (1 << 3) & resv_s_qrv
;
1764 /* s_flag is clear */
1766 if (PIM_INADDR_IS_ANY(group_addr
)) {
1767 /* this is a general query */
1768 /* log that general query should have the s_flag set */
1770 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1771 from_str
, ifp
->name
);
1773 struct igmp_group
*group
;
1775 /* this is a non-general query: perform timer updates */
1777 group
= find_group_by_addr(igmp
, group_addr
);
1779 int recv_num_sources
= ntohs(*(
1782 + IGMP_V3_NUMSOURCES_OFFSET
));
1785 * RFC 3376: 6.6.1. Timer Updates
1786 * Query Q(G,A): Source Timer for sources in A
1787 * are lowered to LMQT
1788 * Query Q(G): Group Timer is lowered to LMQT
1790 if (recv_num_sources
< 1) {
1791 /* Query Q(G): Group Timer is lowered to
1794 igmp_group_timer_lower_to_lmqt(group
);
1796 /* Query Q(G,A): Source Timer for
1797 * sources in A are lowered to LMQT */
1799 /* Scan sources in query and lower their
1801 struct in_addr
*sources
=
1804 + IGMP_V3_SOURCES_OFFSET
);
1805 for (i
= 0; i
< recv_num_sources
; ++i
) {
1806 struct in_addr src_addr
;
1807 struct igmp_source
*src
;
1808 memcpy(&src_addr
, sources
+ i
,
1809 sizeof(struct in_addr
));
1810 src
= igmp_find_source_by_addr(
1813 igmp_source_timer_lower_to_lmqt(
1819 char group_str
[INET_ADDRSTRLEN
];
1820 pim_inet4_dump("<group?>", group_addr
,
1821 group_str
, sizeof(group_str
));
1823 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1824 from_str
, ifp
->name
, group_str
);
1827 } /* s_flag is clear: timer updates */
1830 int igmp_v3_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
1831 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1833 uint16_t recv_checksum
;
1836 uint8_t *group_record
;
1837 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1838 struct interface
*ifp
= igmp
->interface
;
1841 struct pim_interface
*pim_ifp
;
1843 if (igmp
->mtrace_only
)
1846 pim_ifp
= igmp
->interface
->info
;
1848 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1850 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1851 from_str
, ifp
->name
, igmp_msg_len
,
1852 IGMP_V3_MSG_MIN_SIZE
);
1856 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1858 /* for computing checksum */
1859 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1861 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1862 if (checksum
!= recv_checksum
) {
1864 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1865 from_str
, ifp
->name
, recv_checksum
, checksum
);
1869 /* Collecting IGMP Rx stats */
1870 igmp
->rx_stats
.report_v3
++;
1873 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1874 if (num_groups
< 1) {
1876 "Recv IGMP report v3 from %s on %s: missing group records",
1877 from_str
, ifp
->name
);
1881 if (PIM_DEBUG_IGMP_PACKETS
) {
1883 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1884 from_str
, ifp
->name
, igmp_msg_len
, checksum
,
1888 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1891 for (i
= 0; i
< num_groups
; ++i
) {
1892 struct in_addr rec_group
;
1897 int rec_num_sources
;
1901 bool filtered
= false;
1903 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1906 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1907 from_str
, ifp
->name
);
1911 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1913 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1914 rec_num_sources
= ntohs(*(
1915 uint16_t *)(group_record
1916 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1919 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1920 sizeof(struct in_addr
));
1922 if (PIM_DEBUG_IGMP_PACKETS
) {
1924 "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1925 from_str
, ifp
->name
, i
, rec_type
,
1926 rec_auxdatalen
, rec_num_sources
,
1927 inet_ntoa(rec_group
));
1932 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1934 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1936 if ((src
+ 4) > report_pastend
) {
1938 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1939 from_str
, ifp
->name
);
1943 if (PIM_DEBUG_IGMP_PACKETS
) {
1946 if (!inet_ntop(AF_INET
, src
, src_str
,
1948 sprintf(src_str
, "<source?>");
1951 "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1952 from_str
, ifp
->name
, i
,
1953 inet_ntoa(rec_group
), src_str
);
1955 } /* for (sources) */
1958 lncb
.family
= AF_INET
;
1959 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1960 lncb
.prefixlen
= 24;
1963 g
.u
.prefix4
= rec_group
;
1966 /* determine filtering status for group */
1967 filtered
= pim_is_group_filtered(ifp
->info
, &rec_group
);
1969 if (PIM_DEBUG_IGMP_PACKETS
&& filtered
)
1971 "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
1972 inet_ntoa(rec_group
), from_str
, ifp
->name
,
1973 pim_ifp
->boundary_oil_plist
);
1976 * If we receive a igmp report with the group in 224.0.0.0/24
1977 * then we should ignore it
1979 if (prefix_match(&lncb
, &g
))
1982 if (!local_ncb
&& !filtered
)
1984 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1985 igmpv3_report_isin(igmp
, from
, rec_group
,
1987 (struct in_addr
*)sources
);
1989 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1991 igmp
, from
, rec_group
, rec_num_sources
,
1992 (struct in_addr
*)sources
, 0);
1994 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1995 igmpv3_report_toin(igmp
, from
, rec_group
,
1997 (struct in_addr
*)sources
);
1999 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
2000 igmpv3_report_toex(igmp
, from
, rec_group
,
2002 (struct in_addr
*)sources
);
2004 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2005 igmpv3_report_allow(igmp
, from
, rec_group
,
2007 (struct in_addr
*)sources
);
2009 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2010 igmpv3_report_block(igmp
, from
, rec_group
,
2012 (struct in_addr
*)sources
);
2016 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2017 from_str
, ifp
->name
, rec_type
);
2021 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2024 } /* for (group records) */