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
26 #include "pim_iface.h"
28 #include "pim_igmpv3.h"
32 #include "pim_zebra.h"
35 static void group_retransmit_timer_on(struct igmp_group
*group
);
36 static long igmp_group_timer_remain_msec(struct igmp_group
*group
);
37 static long igmp_source_timer_remain_msec(struct igmp_source
*source
);
38 static void group_query_send(struct igmp_group
*group
);
39 static void source_query_send_by_flag(struct igmp_group
*group
,
40 int num_sources_tosend
);
42 static void on_trace(const char *label
, struct interface
*ifp
,
43 struct in_addr from
, struct in_addr group_addr
,
44 int num_sources
, struct in_addr
*sources
)
46 if (PIM_DEBUG_IGMP_TRACE
) {
47 char from_str
[INET_ADDRSTRLEN
];
48 char group_str
[INET_ADDRSTRLEN
];
50 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
51 pim_inet4_dump("<group?>", group_addr
, group_str
,
54 zlog_debug("%s: from %s on %s: group=%s sources=%d", label
,
55 from_str
, ifp
->name
, group_str
, num_sources
);
59 void igmp_group_reset_gmi(struct igmp_group
*group
)
61 long group_membership_interval_msec
;
62 struct pim_interface
*pim_ifp
;
63 struct igmp_sock
*igmp
;
64 struct interface
*ifp
;
66 igmp
= group
->group_igmp_sock
;
67 ifp
= igmp
->interface
;
71 RFC 3376: 8.4. Group Membership Interval
73 The Group Membership Interval is the amount of time that must pass
74 before a multicast router decides there are no more members of a
75 group or a particular source on a network.
77 This value MUST be ((the Robustness Variable) times (the Query
78 Interval)) plus (one Query Response Interval).
80 group_membership_interval_msec = querier_robustness_variable *
81 (1000 * querier_query_interval) +
82 100 * query_response_interval_dsec;
84 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
85 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
86 pim_ifp
->igmp_query_max_response_time_dsec
);
88 if (PIM_DEBUG_IGMP_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 zassert(group
->group_filtermode_isexcl
);
107 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
110 static int igmp_source_timer(struct thread
*t
)
112 struct igmp_source
*source
;
113 struct igmp_group
*group
;
115 source
= THREAD_ARG(t
);
117 group
= source
->source_group
;
119 if (PIM_DEBUG_IGMP_TRACE
) {
120 char group_str
[INET_ADDRSTRLEN
];
121 char source_str
[INET_ADDRSTRLEN
];
122 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
124 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
127 "%s: Source timer expired for group %s source %s on %s",
128 __PRETTY_FUNCTION__
, group_str
, source_str
,
129 group
->group_igmp_sock
->interface
->name
);
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
);
175 static void source_timer_off(struct igmp_group
*group
,
176 struct igmp_source
*source
)
178 if (!source
->t_source_timer
)
181 if (PIM_DEBUG_IGMP_TRACE
) {
182 char group_str
[INET_ADDRSTRLEN
];
183 char source_str
[INET_ADDRSTRLEN
];
184 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
186 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
189 "Cancelling TIMER event for group %s source %s on %s",
190 group_str
, source_str
,
191 group
->group_igmp_sock
->interface
->name
);
194 THREAD_OFF(source
->t_source_timer
);
197 static void igmp_source_timer_on(struct igmp_group
*group
,
198 struct igmp_source
*source
, long interval_msec
)
200 source_timer_off(group
, source
);
201 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
203 if (PIM_DEBUG_IGMP_EVENTS
) {
204 char group_str
[INET_ADDRSTRLEN
];
205 char source_str
[INET_ADDRSTRLEN
];
206 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
208 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
211 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
212 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
213 source_str
, group
->group_igmp_sock
->interface
->name
);
216 thread_add_timer_msec(master
, igmp_source_timer
, source
, interval_msec
,
217 &source
->t_source_timer
);
220 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
222 Source timer switched from (T == 0) to (T > 0): enable forwarding.
224 igmp_source_forward_start(pim_ifp
->pim
, source
);
227 void igmp_source_reset_gmi(struct igmp_sock
*igmp
, struct igmp_group
*group
,
228 struct igmp_source
*source
)
230 long group_membership_interval_msec
;
231 struct pim_interface
*pim_ifp
;
232 struct interface
*ifp
;
234 ifp
= igmp
->interface
;
237 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
238 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
239 pim_ifp
->igmp_query_max_response_time_dsec
);
241 if (PIM_DEBUG_IGMP_TRACE
) {
242 char group_str
[INET_ADDRSTRLEN
];
243 char source_str
[INET_ADDRSTRLEN
];
245 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
247 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
251 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
252 source_str
, group_membership_interval_msec
/ 1000,
253 group_membership_interval_msec
% 1000, group_str
,
257 igmp_source_timer_on(group
, source
, group_membership_interval_msec
);
260 static void source_mark_delete_flag(struct igmp_group
*group
)
262 struct listnode
*src_node
;
263 struct igmp_source
*src
;
265 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
266 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
270 static void source_mark_send_flag(struct igmp_group
*group
)
272 struct listnode
*src_node
;
273 struct igmp_source
*src
;
275 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
276 IGMP_SOURCE_DO_SEND(src
->source_flags
);
280 static int source_mark_send_flag_by_timer(struct igmp_group
*group
)
282 struct listnode
*src_node
;
283 struct igmp_source
*src
;
284 int num_marked_sources
= 0;
286 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
287 /* Is source timer running? */
288 if (src
->t_source_timer
) {
289 IGMP_SOURCE_DO_SEND(src
->source_flags
);
290 ++num_marked_sources
;
292 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
296 return num_marked_sources
;
299 static void source_clear_send_flag(struct list
*source_list
)
301 struct listnode
*src_node
;
302 struct igmp_source
*src
;
304 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
305 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
310 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
312 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
314 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
316 zassert(group
->group_filtermode_isexcl
);
318 if (listcount(group
->group_source_list
) < 1) {
319 igmp_anysource_forward_start(pim_ifp
->pim
, group
);
323 void igmp_source_free(struct igmp_source
*source
)
325 /* make sure there is no source timer running */
326 zassert(!source
->t_source_timer
);
328 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
331 static void source_channel_oil_detach(struct igmp_source
*source
)
333 if (source
->source_channel_oil
) {
334 pim_channel_oil_del(source
->source_channel_oil
);
335 source
->source_channel_oil
= NULL
;
340 igmp_source_delete: stop fowarding, and delete the source
341 igmp_source_forward_stop: stop fowarding, but keep the source
343 void igmp_source_delete(struct igmp_source
*source
)
345 struct igmp_group
*group
;
348 group
= source
->source_group
;
350 if (PIM_DEBUG_IGMP_TRACE
) {
351 char group_str
[INET_ADDRSTRLEN
];
352 char source_str
[INET_ADDRSTRLEN
];
353 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
355 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
358 "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
359 source_str
, group_str
, group
->group_igmp_sock
->fd
,
360 group
->group_igmp_sock
->interface
->name
,
361 source
->source_channel_oil
362 ? source
->source_channel_oil
->oil_ref_count
366 source_timer_off(group
, source
);
367 igmp_source_forward_stop(source
);
369 /* sanity check that forwarding has been disabled */
370 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
371 char group_str
[INET_ADDRSTRLEN
];
372 char source_str
[INET_ADDRSTRLEN
];
373 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
375 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
378 "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
379 __PRETTY_FUNCTION__
, source_str
, group_str
,
380 group
->group_igmp_sock
->fd
,
381 group
->group_igmp_sock
->interface
->name
);
385 source_channel_oil_detach(source
);
388 notice that listnode_delete() can't be moved
389 into igmp_source_free() because the later is
390 called by list_delete_all_node()
392 listnode_delete(group
->group_source_list
, source
);
394 src
.s_addr
= source
->source_addr
.s_addr
;
395 igmp_source_free(source
);
397 /* Group source list is empty and current source is * then
398 *,G group going away so do not trigger start */
399 if (group
->group_filtermode_isexcl
400 && (listcount(group
->group_source_list
) != 0)
401 && src
.s_addr
!= INADDR_ANY
) {
402 group_exclude_fwd_anysrc_ifempty(group
);
406 static void source_delete_by_flag(struct list
*source_list
)
408 struct listnode
*src_node
;
409 struct listnode
*src_nextnode
;
410 struct igmp_source
*src
;
412 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
413 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
414 igmp_source_delete(src
);
417 void igmp_source_delete_expired(struct list
*source_list
)
419 struct listnode
*src_node
;
420 struct listnode
*src_nextnode
;
421 struct igmp_source
*src
;
423 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
424 if (!src
->t_source_timer
)
425 igmp_source_delete(src
);
428 struct igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
429 struct in_addr src_addr
)
431 struct listnode
*src_node
;
432 struct igmp_source
*src
;
434 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
435 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
441 struct igmp_source
*source_new(struct igmp_group
*group
,
442 struct in_addr src_addr
)
444 struct igmp_source
*src
;
446 if (PIM_DEBUG_IGMP_TRACE
) {
447 char group_str
[INET_ADDRSTRLEN
];
448 char source_str
[INET_ADDRSTRLEN
];
449 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
451 pim_inet4_dump("<source?>", src_addr
, source_str
,
454 "Creating new IGMP source %s for group %s on socket %d interface %s",
455 source_str
, group_str
, group
->group_igmp_sock
->fd
,
456 group
->group_igmp_sock
->interface
->name
);
459 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
461 src
->t_source_timer
= NULL
;
462 src
->source_group
= group
; /* back pointer */
463 src
->source_addr
= src_addr
;
464 src
->source_creation
= pim_time_monotonic_sec();
465 src
->source_flags
= 0;
466 src
->source_query_retransmit_count
= 0;
467 src
->source_channel_oil
= NULL
;
469 listnode_add(group
->group_source_list
, src
);
471 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
472 igmp_anysource_forward_stop(group
);
477 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
478 struct igmp_group
*group
,
479 struct in_addr src_addr
)
481 struct igmp_source
*src
;
483 src
= igmp_find_source_by_addr(group
, src_addr
);
488 src
= source_new(group
, src_addr
);
496 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
497 struct in_addr group_addr
, int num_sources
,
498 struct in_addr
*sources
)
500 struct igmp_source
*source
;
501 struct igmp_group
*group
;
504 /* non-existant group is created as INCLUDE {empty} */
505 group
= igmp_add_group_by_addr(igmp
, group_addr
);
510 /* scan received sources */
511 for (i
= 0; i
< num_sources
; ++i
) {
512 struct in_addr
*src_addr
;
514 src_addr
= sources
+ i
;
516 source
= add_source_by_addr(igmp
, group
, *src_addr
);
522 RFC 3376: 6.4.1. Reception of Current-State Records
524 When receiving IS_IN reports for groups in EXCLUDE mode is
525 sources should be moved from set with (timers = 0) to set with
528 igmp_source_reset_gmi() below, resetting the source timers to
529 GMI, accomplishes this.
531 igmp_source_reset_gmi(igmp
, group
, source
);
533 } /* scan received sources */
535 if ((num_sources
== 0) && (group
->group_filtermode_isexcl
)
536 && (listcount(group
->group_source_list
) == 1)) {
537 struct in_addr star
= {.s_addr
= INADDR_ANY
};
539 source
= igmp_find_source_by_addr(group
, star
);
541 igmp_source_reset_gmi(igmp
, group
, source
);
545 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
546 struct in_addr group_addr
, int num_sources
,
547 struct in_addr
*sources
)
549 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
550 num_sources
, sources
);
552 allow(igmp
, from
, group_addr
, num_sources
, sources
);
555 static void isex_excl(struct igmp_group
*group
, int num_sources
,
556 struct in_addr
*sources
)
558 struct igmp_source
*source
;
562 zassert(group
->group_filtermode_isexcl
);
564 /* E.1: set deletion flag for known sources (X,Y) */
565 source_mark_delete_flag(group
);
567 /* scan received sources (A) */
568 for (i
= 0; i
< num_sources
; ++i
) {
569 struct in_addr
*src_addr
;
571 src_addr
= sources
+ i
;
573 /* E.2: lookup reported source from (A) in (X,Y) */
574 source
= igmp_find_source_by_addr(group
, *src_addr
);
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 source
= source_new(group
, *src_addr
);
583 /* ugh, internal malloc failure, skip source */
586 zassert(!source
->t_source_timer
); /* timer == 0 */
587 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
589 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
592 } /* scan received sources */
595 * If we are in isexcl mode and num_sources == 0
596 * than that means we have a *,g entry that
597 * needs to be handled
599 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
600 struct in_addr star
= {.s_addr
= INADDR_ANY
};
601 source
= igmp_find_source_by_addr(group
, star
);
603 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
604 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
609 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
610 source_delete_by_flag(group
->group_source_list
);
613 static void isex_incl(struct igmp_group
*group
, int num_sources
,
614 struct in_addr
*sources
)
619 zassert(!group
->group_filtermode_isexcl
);
621 /* I.1: set deletion flag for known sources (A) */
622 source_mark_delete_flag(group
);
624 /* scan received sources (B) */
625 for (i
= 0; i
< num_sources
; ++i
) {
626 struct igmp_source
*source
;
627 struct in_addr
*src_addr
;
629 src_addr
= sources
+ i
;
631 /* I.2: lookup reported source (B) */
632 source
= igmp_find_source_by_addr(group
, *src_addr
);
634 /* I.3: if found, clear deletion flag (A*B) */
635 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
637 /* I.4: if not found, create source with timer=0 (B-A)
639 source
= source_new(group
, *src_addr
);
641 /* ugh, internal malloc failure, skip source */
644 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
647 } /* scan received sources */
649 /* I.5: delete all sources marked with deletion flag (A-B) */
650 source_delete_by_flag(group
->group_source_list
);
652 group
->group_filtermode_isexcl
= 1; /* boolean=true */
654 zassert(group
->group_filtermode_isexcl
);
656 group_exclude_fwd_anysrc_ifempty(group
);
659 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
660 struct in_addr group_addr
, int num_sources
,
661 struct in_addr
*sources
, int from_igmp_v2_report
)
663 struct interface
*ifp
= igmp
->interface
;
664 struct igmp_group
*group
;
666 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
669 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
672 /* non-existant group is created as INCLUDE {empty} */
673 group
= igmp_add_group_by_addr(igmp
, group_addr
);
678 /* So we can display how we learned the group in our show command output
680 if (from_igmp_v2_report
)
681 group
->igmp_version
= 2;
683 if (group
->group_filtermode_isexcl
) {
685 isex_excl(group
, num_sources
, sources
);
688 isex_incl(group
, num_sources
, sources
);
689 zassert(group
->group_filtermode_isexcl
);
692 zassert(group
->group_filtermode_isexcl
);
694 igmp_group_reset_gmi(group
);
697 static void toin_incl(struct igmp_group
*group
, int num_sources
,
698 struct in_addr
*sources
)
700 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
701 int num_sources_tosend
= listcount(group
->group_source_list
);
704 /* Set SEND flag for all known sources (A) */
705 source_mark_send_flag(group
);
707 /* Scan received sources (B) */
708 for (i
= 0; i
< num_sources
; ++i
) {
709 struct igmp_source
*source
;
710 struct in_addr
*src_addr
;
712 src_addr
= sources
+ i
;
714 /* Lookup reported source (B) */
715 source
= igmp_find_source_by_addr(group
, *src_addr
);
717 /* If found, clear SEND flag (A*B) */
718 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
719 --num_sources_tosend
;
721 /* If not found, create new source */
722 source
= source_new(group
, *src_addr
);
724 /* ugh, internal malloc failure, skip source */
730 igmp_source_reset_gmi(igmp
, group
, source
);
733 /* Send sources marked with SEND flag: Q(G,A-B) */
734 if (num_sources_tosend
> 0) {
735 source_query_send_by_flag(group
, num_sources_tosend
);
739 static void toin_excl(struct igmp_group
*group
, int num_sources
,
740 struct in_addr
*sources
)
742 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
743 int num_sources_tosend
;
746 /* Set SEND flag for X (sources with timer > 0) */
747 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
749 /* Scan received sources (A) */
750 for (i
= 0; i
< num_sources
; ++i
) {
751 struct igmp_source
*source
;
752 struct in_addr
*src_addr
;
754 src_addr
= sources
+ i
;
756 /* Lookup reported source (A) */
757 source
= igmp_find_source_by_addr(group
, *src_addr
);
759 if (source
->t_source_timer
) {
760 /* If found and timer running, clear SEND flag
762 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
763 --num_sources_tosend
;
766 /* If not found, create new source */
767 source
= source_new(group
, *src_addr
);
769 /* ugh, internal malloc failure, skip source */
775 igmp_source_reset_gmi(igmp
, group
, source
);
778 /* Send sources marked with SEND flag: Q(G,X-A) */
779 if (num_sources_tosend
> 0) {
780 source_query_send_by_flag(group
, num_sources_tosend
);
784 group_query_send(group
);
787 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
788 struct in_addr group_addr
, int num_sources
,
789 struct in_addr
*sources
)
791 struct interface
*ifp
= igmp
->interface
;
792 struct igmp_group
*group
;
794 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
798 * If the requested filter mode is INCLUDE *and* the requested source
799 * list is empty, then the entry corresponding to the requested
800 * interface and multicast address is deleted if present. If no such
801 * entry is present, the request is ignored.
804 /* non-existant group is created as INCLUDE {empty} */
805 group
= igmp_add_group_by_addr(igmp
, group_addr
);
810 group
= find_group_by_addr(igmp
, group_addr
);
815 if (group
->group_filtermode_isexcl
) {
817 toin_excl(group
, num_sources
, sources
);
820 toin_incl(group
, num_sources
, sources
);
824 static void toex_incl(struct igmp_group
*group
, int num_sources
,
825 struct in_addr
*sources
)
827 int num_sources_tosend
= 0;
830 zassert(!group
->group_filtermode_isexcl
);
832 /* Set DELETE flag for all known sources (A) */
833 source_mark_delete_flag(group
);
835 /* Clear off SEND flag from all known sources (A) */
836 source_clear_send_flag(group
->group_source_list
);
838 /* Scan received sources (B) */
839 for (i
= 0; i
< num_sources
; ++i
) {
840 struct igmp_source
*source
;
841 struct in_addr
*src_addr
;
843 src_addr
= sources
+ i
;
845 /* Lookup reported source (B) */
846 source
= igmp_find_source_by_addr(group
, *src_addr
);
848 /* If found, clear deletion flag: (A*B) */
849 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
850 /* and set SEND flag (A*B) */
851 IGMP_SOURCE_DO_SEND(source
->source_flags
);
852 ++num_sources_tosend
;
854 /* If source not found, create source with timer=0:
856 source
= source_new(group
, *src_addr
);
858 /* ugh, internal malloc failure, skip source */
861 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
864 } /* Scan received sources (B) */
866 group
->group_filtermode_isexcl
= 1; /* boolean=true */
868 /* Delete all sources marked with DELETE flag (A-B) */
869 source_delete_by_flag(group
->group_source_list
);
871 /* Send sources marked with SEND flag: Q(G,A*B) */
872 if (num_sources_tosend
> 0) {
873 source_query_send_by_flag(group
, num_sources_tosend
);
876 zassert(group
->group_filtermode_isexcl
);
878 group_exclude_fwd_anysrc_ifempty(group
);
881 static void toex_excl(struct igmp_group
*group
, int num_sources
,
882 struct in_addr
*sources
)
884 int num_sources_tosend
= 0;
887 /* set DELETE flag for all known sources (X,Y) */
888 source_mark_delete_flag(group
);
890 /* clear off SEND flag from all known sources (X,Y) */
891 source_clear_send_flag(group
->group_source_list
);
893 if (num_sources
== 0) {
894 struct igmp_source
*source
;
895 struct in_addr any
= {.s_addr
= INADDR_ANY
};
897 source
= igmp_find_source_by_addr(group
, any
);
899 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
902 /* scan received sources (A) */
903 for (i
= 0; i
< num_sources
; ++i
) {
904 struct igmp_source
*source
;
905 struct in_addr
*src_addr
;
907 src_addr
= sources
+ i
;
909 /* lookup reported source (A) in known sources (X,Y) */
910 source
= igmp_find_source_by_addr(group
, *src_addr
);
912 /* if found, clear off DELETE flag from reported source
914 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
916 /* if not found, create source with Group Timer:
917 * (A-X-Y)=Group Timer */
918 long group_timer_msec
;
919 source
= source_new(group
, *src_addr
);
921 /* ugh, internal malloc failure, skip source */
925 zassert(!source
->t_source_timer
); /* timer == 0 */
926 group_timer_msec
= igmp_group_timer_remain_msec(group
);
927 igmp_source_timer_on(group
, source
, group_timer_msec
);
928 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
930 /* make sure source is created with DELETE flag unset */
931 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
934 /* make sure reported source has DELETE flag unset */
935 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
937 if (source
->t_source_timer
) {
938 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
939 IGMP_SOURCE_DO_SEND(source
->source_flags
);
940 ++num_sources_tosend
;
943 } /* scan received sources (A) */
946 delete all sources marked with DELETE flag:
950 source_delete_by_flag(group
->group_source_list
);
952 /* send sources marked with SEND flag: Q(G,A-Y) */
953 if (num_sources_tosend
> 0) {
954 source_query_send_by_flag(group
, num_sources_tosend
);
958 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
959 struct in_addr group_addr
, int num_sources
,
960 struct in_addr
*sources
)
962 struct interface
*ifp
= igmp
->interface
;
963 struct igmp_group
*group
;
965 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
968 /* non-existant group is created as INCLUDE {empty} */
969 group
= igmp_add_group_by_addr(igmp
, group_addr
);
974 if (group
->group_filtermode_isexcl
) {
976 toex_excl(group
, num_sources
, sources
);
979 toex_incl(group
, num_sources
, sources
);
980 zassert(group
->group_filtermode_isexcl
);
982 zassert(group
->group_filtermode_isexcl
);
984 /* Group Timer=GMI */
985 igmp_group_reset_gmi(group
);
988 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
989 struct in_addr group_addr
, int num_sources
,
990 struct in_addr
*sources
)
992 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
993 num_sources
, sources
);
995 allow(igmp
, from
, group_addr
, num_sources
, sources
);
999 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1001 When transmitting a group specific query, if the group timer is
1002 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1003 in the query message.
1005 static void group_retransmit_group(struct igmp_group
*group
)
1007 struct igmp_sock
*igmp
;
1008 struct pim_interface
*pim_ifp
;
1009 long lmqc
; /* Last Member Query Count */
1010 long lmqi_msec
; /* Last Member Query Interval */
1011 long lmqt_msec
; /* Last Member Query Time */
1015 igmp
= group
->group_igmp_sock
;
1016 pim_ifp
= igmp
->interface
->info
;
1018 if (pim_ifp
->igmp_version
== 3) {
1019 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1021 query_buf_size
= IGMP_V12_MSG_SIZE
;
1024 char query_buf
[query_buf_size
];
1026 lmqc
= igmp
->querier_robustness_variable
;
1027 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1028 lmqt_msec
= lmqc
* lmqi_msec
;
1031 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1033 When transmitting a group specific query, if the group timer is
1034 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1035 in the query message.
1037 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1039 if (PIM_DEBUG_IGMP_TRACE
) {
1040 char group_str
[INET_ADDRSTRLEN
];
1041 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1044 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1045 group_str
, igmp
->interface
->name
, s_flag
,
1046 group
->group_specific_query_retransmit_count
);
1050 RFC3376: 4.1.12. IP Destination Addresses for Queries
1052 Group-Specific and Group-and-Source-Specific Queries are sent with
1053 an IP destination address equal to the multicast address of
1057 igmp_send_query(pim_ifp
->igmp_version
, group
, igmp
->fd
,
1058 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
1059 0 /* num_sources_tosend */,
1060 group
->group_addr
/* dst_addr */,
1061 group
->group_addr
/* group_addr */,
1062 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1063 s_flag
, igmp
->querier_robustness_variable
,
1064 igmp
->querier_query_interval
);
1068 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1070 When building a group and source specific query for a group G, two
1071 separate query messages are sent for the group. The first one has
1072 the "Suppress Router-Side Processing" bit set and contains all the
1073 sources with retransmission state and timers greater than LMQT. The
1074 second has the "Suppress Router-Side Processing" bit clear and
1075 contains all the sources with retransmission state and timers lower
1076 or equal to LMQT. If either of the two calculated messages does not
1077 contain any sources, then its transmission is suppressed.
1079 static int group_retransmit_sources(struct igmp_group
*group
,
1080 int send_with_sflag_set
)
1082 struct igmp_sock
*igmp
;
1083 struct pim_interface
*pim_ifp
;
1084 long lmqc
; /* Last Member Query Count */
1085 long lmqi_msec
; /* Last Member Query Interval */
1086 long lmqt_msec
; /* Last Member Query Time */
1087 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1088 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1089 int query_buf1_max_sources
;
1090 int query_buf2_max_sources
;
1091 struct in_addr
*source_addr1
;
1092 struct in_addr
*source_addr2
;
1093 int num_sources_tosend1
;
1094 int num_sources_tosend2
;
1095 struct listnode
*src_node
;
1096 struct igmp_source
*src
;
1097 int num_retransmit_sources_left
= 0;
1099 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1100 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1102 igmp
= group
->group_igmp_sock
;
1103 pim_ifp
= igmp
->interface
->info
;
1105 lmqc
= igmp
->querier_robustness_variable
;
1106 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1107 lmqt_msec
= lmqc
* lmqi_msec
;
1109 /* Scan all group sources */
1110 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1112 /* Source has retransmission state? */
1113 if (src
->source_query_retransmit_count
< 1)
1116 if (--src
->source_query_retransmit_count
> 0) {
1117 ++num_retransmit_sources_left
;
1120 /* Copy source address into appropriate query buffer */
1121 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1122 *source_addr1
= src
->source_addr
;
1125 *source_addr2
= src
->source_addr
;
1130 num_sources_tosend1
=
1132 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1133 num_sources_tosend2
=
1135 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1137 if (PIM_DEBUG_IGMP_TRACE
) {
1138 char group_str
[INET_ADDRSTRLEN
];
1139 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1142 "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",
1143 group_str
, igmp
->interface
->name
, num_sources_tosend1
,
1144 num_sources_tosend2
, send_with_sflag_set
,
1145 num_retransmit_sources_left
);
1148 if (num_sources_tosend1
> 0) {
1150 Send group-and-source-specific query with s_flag set and all
1151 sources with timers greater than LMQT.
1154 if (send_with_sflag_set
) {
1156 query_buf1_max_sources
=
1157 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1159 if (num_sources_tosend1
> query_buf1_max_sources
) {
1160 char group_str
[INET_ADDRSTRLEN
];
1161 pim_inet4_dump("<group?>", group
->group_addr
,
1162 group_str
, sizeof(group_str
));
1164 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1165 __PRETTY_FUNCTION__
, group_str
,
1166 igmp
->interface
->name
,
1167 num_sources_tosend1
, sizeof(query_buf1
),
1168 query_buf1_max_sources
);
1171 RFC3376: 4.1.12. IP Destination Addresses for
1174 Group-Specific and Group-and-Source-Specific
1175 Queries are sent with
1176 an IP destination address equal to the
1177 multicast address of
1182 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1183 igmp
->interface
->name
, query_buf1
,
1184 sizeof(query_buf1
), num_sources_tosend1
,
1185 group
->group_addr
, group
->group_addr
,
1186 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1188 igmp
->querier_robustness_variable
,
1189 igmp
->querier_query_interval
);
1192 } /* send_with_sflag_set */
1195 if (num_sources_tosend2
> 0) {
1197 Send group-and-source-specific query with s_flag clear and all
1198 sources with timers lower or equal to LMQT.
1201 query_buf2_max_sources
=
1202 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1203 if (num_sources_tosend2
> query_buf2_max_sources
) {
1204 char group_str
[INET_ADDRSTRLEN
];
1205 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1208 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1209 __PRETTY_FUNCTION__
, group_str
,
1210 igmp
->interface
->name
, num_sources_tosend2
,
1211 sizeof(query_buf2
), query_buf2_max_sources
);
1214 RFC3376: 4.1.12. IP Destination Addresses for Queries
1216 Group-Specific and Group-and-Source-Specific Queries
1218 an IP destination address equal to the multicast
1224 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1225 igmp
->interface
->name
, query_buf2
,
1226 sizeof(query_buf2
), num_sources_tosend2
,
1227 group
->group_addr
, group
->group_addr
,
1228 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1230 igmp
->querier_robustness_variable
,
1231 igmp
->querier_query_interval
);
1235 return num_retransmit_sources_left
;
1238 static int igmp_group_retransmit(struct thread
*t
)
1240 struct igmp_group
*group
;
1241 int num_retransmit_sources_left
;
1242 int send_with_sflag_set
; /* boolean */
1244 group
= THREAD_ARG(t
);
1246 if (PIM_DEBUG_IGMP_TRACE
) {
1247 char group_str
[INET_ADDRSTRLEN
];
1248 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1250 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1251 group
->group_igmp_sock
->interface
->name
);
1254 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1255 if (group
->group_specific_query_retransmit_count
> 0) {
1257 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1258 group_retransmit_group(group
);
1259 --group
->group_specific_query_retransmit_count
;
1263 If a group specific query is scheduled to be transmitted at
1265 same time as a group and source specific query for the same
1267 then transmission of the group and source specific message
1269 "Suppress Router-Side Processing" bit set may be suppressed.
1271 send_with_sflag_set
= 0; /* boolean=false */
1273 send_with_sflag_set
= 1; /* boolean=true */
1276 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1277 num_retransmit_sources_left
=
1278 group_retransmit_sources(group
, send_with_sflag_set
);
1281 Keep group retransmit timer running if there is any retransmit
1284 if ((num_retransmit_sources_left
> 0)
1285 || (group
->group_specific_query_retransmit_count
> 0)) {
1286 group_retransmit_timer_on(group
);
1293 group_retransmit_timer_on:
1294 if group retransmit timer isn't running, starts it;
1295 otherwise, do nothing
1297 static void group_retransmit_timer_on(struct igmp_group
*group
)
1299 struct igmp_sock
*igmp
;
1300 struct pim_interface
*pim_ifp
;
1301 long lmqi_msec
; /* Last Member Query Interval */
1303 /* if group retransmit timer is running, do nothing */
1304 if (group
->t_group_query_retransmit_timer
) {
1308 igmp
= group
->group_igmp_sock
;
1309 pim_ifp
= igmp
->interface
->info
;
1311 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1313 if (PIM_DEBUG_IGMP_TRACE
) {
1314 char group_str
[INET_ADDRSTRLEN
];
1315 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1318 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1319 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1320 igmp
->interface
->name
);
1323 thread_add_timer_msec(master
, igmp_group_retransmit
, group
, lmqi_msec
,
1324 &group
->t_group_query_retransmit_timer
);
1327 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1329 return pim_time_timer_remain_msec(group
->t_group_timer
);
1332 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1334 return pim_time_timer_remain_msec(source
->t_source_timer
);
1338 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1340 static void group_query_send(struct igmp_group
*group
)
1342 long lmqc
; /* Last Member Query Count */
1344 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1346 /* lower group timer to lmqt */
1347 igmp_group_timer_lower_to_lmqt(group
);
1349 /* reset retransmission counter */
1350 group
->group_specific_query_retransmit_count
= lmqc
;
1352 /* immediately send group specific query (decrease retransmit counter by
1354 group_retransmit_group(group
);
1356 /* make sure group retransmit timer is running */
1357 group_retransmit_timer_on(group
);
1361 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1363 static void source_query_send_by_flag(struct igmp_group
*group
,
1364 int num_sources_tosend
)
1366 struct igmp_sock
*igmp
;
1367 struct pim_interface
*pim_ifp
;
1368 struct listnode
*src_node
;
1369 struct igmp_source
*src
;
1370 long lmqc
; /* Last Member Query Count */
1371 long lmqi_msec
; /* Last Member Query Interval */
1372 long lmqt_msec
; /* Last Member Query Time */
1374 zassert(num_sources_tosend
> 0);
1376 igmp
= group
->group_igmp_sock
;
1377 pim_ifp
= igmp
->interface
->info
;
1379 lmqc
= igmp
->querier_robustness_variable
;
1380 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1381 lmqt_msec
= lmqc
* lmqi_msec
;
1384 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1387 (...) for each of the sources in X of group G, with source timer
1390 o Set number of retransmissions for each source to [Last Member
1392 o Lower source timer to LMQT.
1394 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1395 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1396 /* source "src" in X of group G */
1397 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1398 src
->source_query_retransmit_count
= lmqc
;
1399 igmp_source_timer_lower_to_lmqt(src
);
1404 /* send group-and-source specific queries */
1405 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1407 /* make sure group retransmit timer is running */
1408 group_retransmit_timer_on(group
);
1411 static void block_excl(struct igmp_group
*group
, int num_sources
,
1412 struct in_addr
*sources
)
1414 int num_sources_tosend
= 0;
1417 /* 1. clear off SEND flag from all known sources (X,Y) */
1418 source_clear_send_flag(group
->group_source_list
);
1420 /* 2. scan received sources (A) */
1421 for (i
= 0; i
< num_sources
; ++i
) {
1422 struct igmp_source
*source
;
1423 struct in_addr
*src_addr
;
1425 src_addr
= sources
+ i
;
1427 /* lookup reported source (A) in known sources (X,Y) */
1428 source
= igmp_find_source_by_addr(group
, *src_addr
);
1430 /* 3: if not found, create source with Group Timer:
1431 * (A-X-Y)=Group Timer */
1432 long group_timer_msec
;
1433 source
= source_new(group
, *src_addr
);
1435 /* ugh, internal malloc failure, skip source */
1439 zassert(!source
->t_source_timer
); /* timer == 0 */
1440 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1441 igmp_source_timer_on(group
, source
, group_timer_msec
);
1442 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1445 if (source
->t_source_timer
) {
1446 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1447 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1448 ++num_sources_tosend
;
1452 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1453 if (num_sources_tosend
> 0) {
1454 source_query_send_by_flag(group
, num_sources_tosend
);
1458 static void block_incl(struct igmp_group
*group
, int num_sources
,
1459 struct in_addr
*sources
)
1461 int num_sources_tosend
= 0;
1464 /* 1. clear off SEND flag from all known sources (B) */
1465 source_clear_send_flag(group
->group_source_list
);
1467 /* 2. scan received sources (A) */
1468 for (i
= 0; i
< num_sources
; ++i
) {
1469 struct igmp_source
*source
;
1470 struct in_addr
*src_addr
;
1472 src_addr
= sources
+ i
;
1474 /* lookup reported source (A) in known sources (B) */
1475 source
= igmp_find_source_by_addr(group
, *src_addr
);
1477 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1478 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1479 ++num_sources_tosend
;
1483 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1484 if (num_sources_tosend
> 0) {
1485 source_query_send_by_flag(group
, num_sources_tosend
);
1489 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1490 struct in_addr group_addr
, int num_sources
,
1491 struct in_addr
*sources
)
1493 struct interface
*ifp
= igmp
->interface
;
1494 struct igmp_group
*group
;
1496 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
1499 /* non-existant group is created as INCLUDE {empty} */
1500 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1505 if (group
->group_filtermode_isexcl
) {
1507 block_excl(group
, num_sources
, sources
);
1510 block_incl(group
, num_sources
, sources
);
1514 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1516 struct igmp_sock
*igmp
;
1517 struct interface
*ifp
;
1518 struct pim_interface
*pim_ifp
;
1520 int lmqi_dsec
; /* Last Member Query Interval */
1521 int lmqc
; /* Last Member Query Count */
1522 int lmqt_msec
; /* Last Member Query Time */
1525 RFC 3376: 6.2.2. Definition of Group Timers
1527 The group timer is only used when a group is in EXCLUDE mode and
1528 it represents the time for the *filter-mode* of the group to
1529 expire and switch to INCLUDE mode.
1531 if (!group
->group_filtermode_isexcl
) {
1535 igmp
= group
->group_igmp_sock
;
1536 ifp
= igmp
->interface
;
1537 pim_ifp
= ifp
->info
;
1540 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1541 lmqc
= igmp
->querier_robustness_variable
;
1542 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1543 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1545 if (PIM_DEBUG_IGMP_TRACE
) {
1546 char group_str
[INET_ADDRSTRLEN
];
1547 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1550 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1551 __PRETTY_FUNCTION__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1555 zassert(group
->group_filtermode_isexcl
);
1557 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1560 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1562 struct igmp_group
*group
;
1563 struct igmp_sock
*igmp
;
1564 struct interface
*ifp
;
1565 struct pim_interface
*pim_ifp
;
1567 int lmqi_dsec
; /* Last Member Query Interval */
1568 int lmqc
; /* Last Member Query Count */
1569 int lmqt_msec
; /* Last Member Query Time */
1571 group
= source
->source_group
;
1572 igmp
= group
->group_igmp_sock
;
1573 ifp
= igmp
->interface
;
1574 pim_ifp
= ifp
->info
;
1577 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1578 lmqc
= igmp
->querier_robustness_variable
;
1579 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1580 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1582 if (PIM_DEBUG_IGMP_TRACE
) {
1583 char group_str
[INET_ADDRSTRLEN
];
1584 char source_str
[INET_ADDRSTRLEN
];
1585 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1587 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1588 sizeof(source_str
));
1590 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1591 __PRETTY_FUNCTION__
, group_str
, source_str
, ifname
,
1592 lmqc
, lmqi_dsec
, lmqt_msec
);
1595 igmp_source_timer_on(group
, source
, lmqt_msec
);
1598 void igmp_v3_send_query(struct igmp_group
*group
, int fd
, const char *ifname
,
1599 char *query_buf
, int query_buf_size
, int num_sources
,
1600 struct in_addr dst_addr
, struct in_addr group_addr
,
1601 int query_max_response_time_dsec
, uint8_t s_flag
,
1602 uint8_t querier_robustness_variable
,
1603 uint16_t querier_query_interval
)
1606 uint8_t max_resp_code
;
1609 struct sockaddr_in to
;
1613 zassert(num_sources
>= 0);
1615 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1616 if (msg_size
> query_buf_size
) {
1618 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1619 __FILE__
, __PRETTY_FUNCTION__
, msg_size
,
1624 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1625 zassert((s_flag
== 0) || (s_flag
== 1));
1627 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1628 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1631 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1633 If non-zero, the QRV field contains the [Robustness Variable]
1634 value used by the querier, i.e., the sender of the Query. If the
1635 querier's [Robustness Variable] exceeds 7, the maximum value of
1636 the QRV field, the QRV is set to zero.
1638 if (querier_robustness_variable
> 7) {
1639 querier_robustness_variable
= 0;
1642 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1643 query_buf
[1] = max_resp_code
;
1644 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1645 0; /* for computing checksum */
1646 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1648 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1649 query_buf
[9] = qqic
;
1650 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1653 checksum
= in_cksum(query_buf
, msg_size
);
1654 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1656 if (PIM_DEBUG_IGMP_PACKETS
) {
1657 char dst_str
[INET_ADDRSTRLEN
];
1658 char group_str
[INET_ADDRSTRLEN
];
1659 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1660 pim_inet4_dump("<group?>", group_addr
, group_str
,
1663 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1664 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1665 s_flag
, querier_robustness_variable
,
1666 querier_query_interval
, qqic
);
1669 memset(&to
, 0, sizeof(to
));
1670 to
.sin_family
= AF_INET
;
1671 to
.sin_addr
= dst_addr
;
1674 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1675 (struct sockaddr
*)&to
, tolen
);
1676 if (sent
!= (ssize_t
)msg_size
) {
1677 char dst_str
[INET_ADDRSTRLEN
];
1678 char group_str
[INET_ADDRSTRLEN
];
1679 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1680 pim_inet4_dump("<group?>", group_addr
, group_str
,
1684 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1685 dst_str
, ifname
, group_str
, msg_size
, errno
,
1686 safe_strerror(errno
));
1689 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1690 dst_str
, ifname
, group_str
, msg_size
, sent
);
1696 s_flag sanity test: s_flag must be set for general queries
1698 RFC 3376: 6.6.1. Timer Updates
1700 When a router sends or receives a query with a clear Suppress
1701 Router-Side Processing flag, it must update its timers to reflect
1702 the correct timeout values for the group or sources being queried.
1704 General queries don't trigger timer update.
1707 /* general query? */
1708 if (PIM_INADDR_IS_ANY(group_addr
)) {
1709 char dst_str
[INET_ADDRSTRLEN
];
1710 char group_str
[INET_ADDRSTRLEN
];
1711 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1713 pim_inet4_dump("<group?>", group_addr
, group_str
,
1716 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1717 __PRETTY_FUNCTION__
, dst_str
, ifname
, group_str
,
1723 void igmp_v3_recv_query(struct igmp_sock
*igmp
, const char *from_str
,
1726 struct interface
*ifp
;
1727 struct pim_interface
*pim_ifp
;
1728 struct in_addr group_addr
;
1729 uint8_t resv_s_qrv
= 0;
1734 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1735 ifp
= igmp
->interface
;
1736 pim_ifp
= ifp
->info
;
1739 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1741 * Routers adopt the QRV value from the most recently received Query
1742 * as their own [Robustness Variable] value, unless that most
1743 * recently received QRV was zero, in which case the receivers use
1744 * the default [Robustness Variable] value specified in section 8.1
1745 * or a statically configured value.
1747 resv_s_qrv
= igmp_msg
[8];
1748 qrv
= 7 & resv_s_qrv
;
1749 igmp
->querier_robustness_variable
=
1750 qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1753 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1755 * Multicast routers that are not the current querier adopt the QQI
1756 * value from the most recently received Query as their own [Query
1757 * Interval] value, unless that most recently received QQI was zero,
1758 * in which case the receiving routers use the default.
1760 if (igmp
->t_other_querier_timer
) {
1761 /* other querier present */
1765 qqi
= igmp_msg_decode8to16(qqic
);
1766 igmp
->querier_query_interval
=
1767 qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1769 if (PIM_DEBUG_IGMP_TRACE
) {
1770 char ifaddr_str
[INET_ADDRSTRLEN
];
1771 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1772 sizeof(ifaddr_str
));
1774 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1776 qqi
? "recv-non-default" : "default",
1777 igmp
->querier_query_interval
, qqic
, from_str
);
1782 * RFC 3376: 6.6.1. Timer Updates
1784 * When a router sends or receives a query with a clear Suppress
1785 * Router-Side Processing flag, it must update its timers to reflect
1786 * the correct timeout values for the group or sources being queried.
1788 * General queries don't trigger timer update.
1790 s_flag
= (1 << 3) & resv_s_qrv
;
1793 /* s_flag is clear */
1795 if (PIM_INADDR_IS_ANY(group_addr
)) {
1796 /* this is a general query */
1797 /* log that general query should have the s_flag set */
1799 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1800 from_str
, ifp
->name
);
1802 struct igmp_group
*group
;
1804 /* this is a non-general query: perform timer updates */
1806 group
= find_group_by_addr(igmp
, group_addr
);
1808 int recv_num_sources
= ntohs(*(
1811 + IGMP_V3_NUMSOURCES_OFFSET
));
1814 * RFC 3376: 6.6.1. Timer Updates
1815 * Query Q(G,A): Source Timer for sources in A
1816 * are lowered to LMQT
1817 * Query Q(G): Group Timer is lowered to LMQT
1819 if (recv_num_sources
< 1) {
1820 /* Query Q(G): Group Timer is lowered to
1823 igmp_group_timer_lower_to_lmqt(group
);
1825 /* Query Q(G,A): Source Timer for
1826 * sources in A are lowered to LMQT */
1828 /* Scan sources in query and lower their
1830 struct in_addr
*sources
=
1833 + IGMP_V3_SOURCES_OFFSET
);
1834 for (i
= 0; i
< recv_num_sources
; ++i
) {
1835 struct in_addr src_addr
;
1836 struct igmp_source
*src
;
1837 memcpy(&src_addr
, sources
+ i
,
1838 sizeof(struct in_addr
));
1839 src
= igmp_find_source_by_addr(
1842 igmp_source_timer_lower_to_lmqt(
1848 char group_str
[INET_ADDRSTRLEN
];
1849 pim_inet4_dump("<group?>", group_addr
,
1850 group_str
, sizeof(group_str
));
1852 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1853 from_str
, ifp
->name
, group_str
);
1856 } /* s_flag is clear: timer updates */
1859 int igmp_v3_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
1860 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1862 uint16_t recv_checksum
;
1865 uint8_t *group_record
;
1866 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1867 struct interface
*ifp
= igmp
->interface
;
1870 struct pim_interface
*pim_ifp
;
1872 if (igmp
->mtrace_only
)
1875 pim_ifp
= igmp
->interface
->info
;
1877 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1879 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1880 from_str
, ifp
->name
, igmp_msg_len
,
1881 IGMP_V3_MSG_MIN_SIZE
);
1885 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1887 /* for computing checksum */
1888 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1890 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1891 if (checksum
!= recv_checksum
) {
1893 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1894 from_str
, ifp
->name
, recv_checksum
, checksum
);
1898 /* Collecting IGMP Rx stats */
1899 igmp
->rx_stats
.report_v3
++;
1902 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1903 if (num_groups
< 1) {
1905 "Recv IGMP report v3 from %s on %s: missing group records",
1906 from_str
, ifp
->name
);
1910 if (PIM_DEBUG_IGMP_PACKETS
) {
1912 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1913 from_str
, ifp
->name
, igmp_msg_len
, checksum
,
1917 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1920 for (i
= 0; i
< num_groups
; ++i
) {
1921 struct in_addr rec_group
;
1926 int rec_num_sources
;
1930 bool filtered
= false;
1932 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1935 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1936 from_str
, ifp
->name
);
1940 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1942 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1943 rec_num_sources
= ntohs(*(
1944 uint16_t *)(group_record
1945 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1948 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1949 sizeof(struct in_addr
));
1951 if (PIM_DEBUG_IGMP_PACKETS
) {
1953 "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1954 from_str
, ifp
->name
, i
, rec_type
,
1955 rec_auxdatalen
, rec_num_sources
,
1956 inet_ntoa(rec_group
));
1961 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1963 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1965 if ((src
+ 4) > report_pastend
) {
1967 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1968 from_str
, ifp
->name
);
1972 if (PIM_DEBUG_IGMP_PACKETS
) {
1975 if (!inet_ntop(AF_INET
, src
, src_str
,
1977 sprintf(src_str
, "<source?>");
1980 "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1981 from_str
, ifp
->name
, i
,
1982 inet_ntoa(rec_group
), src_str
);
1984 } /* for (sources) */
1987 lncb
.family
= AF_INET
;
1988 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1989 lncb
.prefixlen
= 24;
1992 g
.u
.prefix4
= rec_group
;
1995 /* determine filtering status for group */
1996 filtered
= pim_is_group_filtered(ifp
->info
, &rec_group
);
1998 if (PIM_DEBUG_IGMP_PACKETS
&& filtered
)
2000 "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
2001 inet_ntoa(rec_group
), from_str
, ifp
->name
,
2002 pim_ifp
->boundary_oil_plist
);
2005 * If we receive a igmp report with the group in 224.0.0.0/24
2006 * then we should ignore it
2008 if (prefix_match(&lncb
, &g
))
2011 if (!local_ncb
&& !filtered
)
2013 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
2014 igmpv3_report_isin(igmp
, from
, rec_group
,
2016 (struct in_addr
*)sources
);
2018 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
2020 igmp
, from
, rec_group
, rec_num_sources
,
2021 (struct in_addr
*)sources
, 0);
2023 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
2024 igmpv3_report_toin(igmp
, from
, rec_group
,
2026 (struct in_addr
*)sources
);
2028 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
2029 igmpv3_report_toex(igmp
, from
, rec_group
,
2031 (struct in_addr
*)sources
);
2033 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2034 igmpv3_report_allow(igmp
, from
, rec_group
,
2036 (struct in_addr
*)sources
);
2038 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2039 igmpv3_report_block(igmp
, from
, rec_group
,
2041 (struct in_addr
*)sources
);
2045 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2046 from_str
, ifp
->name
, rec_type
);
2050 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2053 } /* for (group records) */