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 zlog_warn("%s %s: XCALLOC() failure", __FILE__
,
462 __PRETTY_FUNCTION__
);
463 return 0; /* error, not found, could not create */
466 src
->t_source_timer
= NULL
;
467 src
->source_group
= group
; /* back pointer */
468 src
->source_addr
= src_addr
;
469 src
->source_creation
= pim_time_monotonic_sec();
470 src
->source_flags
= 0;
471 src
->source_query_retransmit_count
= 0;
472 src
->source_channel_oil
= NULL
;
474 listnode_add(group
->group_source_list
, src
);
476 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
477 igmp_anysource_forward_stop(group
);
482 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
483 struct igmp_group
*group
,
484 struct in_addr src_addr
)
486 struct igmp_source
*src
;
488 src
= igmp_find_source_by_addr(group
, src_addr
);
493 src
= source_new(group
, src_addr
);
501 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
502 struct in_addr group_addr
, int num_sources
,
503 struct in_addr
*sources
)
505 struct igmp_source
*source
;
506 struct igmp_group
*group
;
509 /* non-existant group is created as INCLUDE {empty} */
510 group
= igmp_add_group_by_addr(igmp
, group_addr
);
515 /* scan received sources */
516 for (i
= 0; i
< num_sources
; ++i
) {
517 struct in_addr
*src_addr
;
519 src_addr
= sources
+ i
;
521 source
= add_source_by_addr(igmp
, group
, *src_addr
);
527 RFC 3376: 6.4.1. Reception of Current-State Records
529 When receiving IS_IN reports for groups in EXCLUDE mode is
530 sources should be moved from set with (timers = 0) to set with
533 igmp_source_reset_gmi() below, resetting the source timers to
534 GMI, accomplishes this.
536 igmp_source_reset_gmi(igmp
, group
, source
);
538 } /* scan received sources */
540 if ((num_sources
== 0) && (group
->group_filtermode_isexcl
)
541 && (listcount(group
->group_source_list
) == 1)) {
542 struct in_addr star
= {.s_addr
= INADDR_ANY
};
544 source
= igmp_find_source_by_addr(group
, star
);
546 igmp_source_reset_gmi(igmp
, group
, source
);
550 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
551 struct in_addr group_addr
, int num_sources
,
552 struct in_addr
*sources
)
554 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
555 num_sources
, sources
);
557 allow(igmp
, from
, group_addr
, num_sources
, sources
);
560 static void isex_excl(struct igmp_group
*group
, int num_sources
,
561 struct in_addr
*sources
)
563 struct igmp_source
*source
;
567 zassert(group
->group_filtermode_isexcl
);
569 /* E.1: set deletion flag for known sources (X,Y) */
570 source_mark_delete_flag(group
);
572 /* scan received sources (A) */
573 for (i
= 0; i
< num_sources
; ++i
) {
574 struct in_addr
*src_addr
;
576 src_addr
= sources
+ i
;
578 /* E.2: lookup reported source from (A) in (X,Y) */
579 source
= igmp_find_source_by_addr(group
, *src_addr
);
581 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
582 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
584 /* E.4: if not found, create source with timer=GMI:
586 source
= source_new(group
, *src_addr
);
588 /* ugh, internal malloc failure, skip source */
591 zassert(!source
->t_source_timer
); /* timer == 0 */
592 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
594 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
597 } /* scan received sources */
600 * If we are in isexcl mode and num_sources == 0
601 * than that means we have a *,g entry that
602 * needs to be handled
604 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
605 struct in_addr star
= {.s_addr
= INADDR_ANY
};
606 source
= igmp_find_source_by_addr(group
, star
);
608 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
609 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
614 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
615 source_delete_by_flag(group
->group_source_list
);
618 static void isex_incl(struct igmp_group
*group
, int num_sources
,
619 struct in_addr
*sources
)
624 zassert(!group
->group_filtermode_isexcl
);
626 /* I.1: set deletion flag for known sources (A) */
627 source_mark_delete_flag(group
);
629 /* scan received sources (B) */
630 for (i
= 0; i
< num_sources
; ++i
) {
631 struct igmp_source
*source
;
632 struct in_addr
*src_addr
;
634 src_addr
= sources
+ i
;
636 /* I.2: lookup reported source (B) */
637 source
= igmp_find_source_by_addr(group
, *src_addr
);
639 /* I.3: if found, clear deletion flag (A*B) */
640 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
642 /* I.4: if not found, create source with timer=0 (B-A)
644 source
= source_new(group
, *src_addr
);
646 /* ugh, internal malloc failure, skip source */
649 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
652 } /* scan received sources */
654 /* I.5: delete all sources marked with deletion flag (A-B) */
655 source_delete_by_flag(group
->group_source_list
);
657 group
->group_filtermode_isexcl
= 1; /* boolean=true */
659 zassert(group
->group_filtermode_isexcl
);
661 group_exclude_fwd_anysrc_ifempty(group
);
664 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
665 struct in_addr group_addr
, int num_sources
,
666 struct in_addr
*sources
, int from_igmp_v2_report
)
668 struct interface
*ifp
= igmp
->interface
;
669 struct igmp_group
*group
;
671 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
674 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
677 /* non-existant group is created as INCLUDE {empty} */
678 group
= igmp_add_group_by_addr(igmp
, group_addr
);
683 /* So we can display how we learned the group in our show command output
685 if (from_igmp_v2_report
)
686 group
->igmp_version
= 2;
688 if (group
->group_filtermode_isexcl
) {
690 isex_excl(group
, num_sources
, sources
);
693 isex_incl(group
, num_sources
, sources
);
694 zassert(group
->group_filtermode_isexcl
);
697 zassert(group
->group_filtermode_isexcl
);
699 igmp_group_reset_gmi(group
);
702 static void toin_incl(struct igmp_group
*group
, int num_sources
,
703 struct in_addr
*sources
)
705 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
706 int num_sources_tosend
= listcount(group
->group_source_list
);
709 /* Set SEND flag for all known sources (A) */
710 source_mark_send_flag(group
);
712 /* Scan received sources (B) */
713 for (i
= 0; i
< num_sources
; ++i
) {
714 struct igmp_source
*source
;
715 struct in_addr
*src_addr
;
717 src_addr
= sources
+ i
;
719 /* Lookup reported source (B) */
720 source
= igmp_find_source_by_addr(group
, *src_addr
);
722 /* If found, clear SEND flag (A*B) */
723 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
724 --num_sources_tosend
;
726 /* If not found, create new source */
727 source
= source_new(group
, *src_addr
);
729 /* ugh, internal malloc failure, skip source */
735 igmp_source_reset_gmi(igmp
, group
, source
);
738 /* Send sources marked with SEND flag: Q(G,A-B) */
739 if (num_sources_tosend
> 0) {
740 source_query_send_by_flag(group
, num_sources_tosend
);
744 static void toin_excl(struct igmp_group
*group
, int num_sources
,
745 struct in_addr
*sources
)
747 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
748 int num_sources_tosend
;
751 /* Set SEND flag for X (sources with timer > 0) */
752 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
754 /* Scan received sources (A) */
755 for (i
= 0; i
< num_sources
; ++i
) {
756 struct igmp_source
*source
;
757 struct in_addr
*src_addr
;
759 src_addr
= sources
+ i
;
761 /* Lookup reported source (A) */
762 source
= igmp_find_source_by_addr(group
, *src_addr
);
764 if (source
->t_source_timer
) {
765 /* If found and timer running, clear SEND flag
767 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
768 --num_sources_tosend
;
771 /* If not found, create new source */
772 source
= source_new(group
, *src_addr
);
774 /* ugh, internal malloc failure, skip source */
780 igmp_source_reset_gmi(igmp
, group
, source
);
783 /* Send sources marked with SEND flag: Q(G,X-A) */
784 if (num_sources_tosend
> 0) {
785 source_query_send_by_flag(group
, num_sources_tosend
);
789 group_query_send(group
);
792 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
793 struct in_addr group_addr
, int num_sources
,
794 struct in_addr
*sources
)
796 struct interface
*ifp
= igmp
->interface
;
797 struct igmp_group
*group
;
799 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
803 * If the requested filter mode is INCLUDE *and* the requested source
804 * list is empty, then the entry corresponding to the requested
805 * interface and multicast address is deleted if present. If no such
806 * entry is present, the request is ignored.
809 /* non-existant group is created as INCLUDE {empty} */
810 group
= igmp_add_group_by_addr(igmp
, group_addr
);
815 group
= find_group_by_addr(igmp
, group_addr
);
820 if (group
->group_filtermode_isexcl
) {
822 toin_excl(group
, num_sources
, sources
);
825 toin_incl(group
, num_sources
, sources
);
829 static void toex_incl(struct igmp_group
*group
, int num_sources
,
830 struct in_addr
*sources
)
832 int num_sources_tosend
= 0;
835 zassert(!group
->group_filtermode_isexcl
);
837 /* Set DELETE flag for all known sources (A) */
838 source_mark_delete_flag(group
);
840 /* Clear off SEND flag from all known sources (A) */
841 source_clear_send_flag(group
->group_source_list
);
843 /* Scan received sources (B) */
844 for (i
= 0; i
< num_sources
; ++i
) {
845 struct igmp_source
*source
;
846 struct in_addr
*src_addr
;
848 src_addr
= sources
+ i
;
850 /* Lookup reported source (B) */
851 source
= igmp_find_source_by_addr(group
, *src_addr
);
853 /* If found, clear deletion flag: (A*B) */
854 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
855 /* and set SEND flag (A*B) */
856 IGMP_SOURCE_DO_SEND(source
->source_flags
);
857 ++num_sources_tosend
;
859 /* If source not found, create source with timer=0:
861 source
= source_new(group
, *src_addr
);
863 /* ugh, internal malloc failure, skip source */
866 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
869 } /* Scan received sources (B) */
871 group
->group_filtermode_isexcl
= 1; /* boolean=true */
873 /* Delete all sources marked with DELETE flag (A-B) */
874 source_delete_by_flag(group
->group_source_list
);
876 /* Send sources marked with SEND flag: Q(G,A*B) */
877 if (num_sources_tosend
> 0) {
878 source_query_send_by_flag(group
, num_sources_tosend
);
881 zassert(group
->group_filtermode_isexcl
);
883 group_exclude_fwd_anysrc_ifempty(group
);
886 static void toex_excl(struct igmp_group
*group
, int num_sources
,
887 struct in_addr
*sources
)
889 int num_sources_tosend
= 0;
892 /* set DELETE flag for all known sources (X,Y) */
893 source_mark_delete_flag(group
);
895 /* clear off SEND flag from all known sources (X,Y) */
896 source_clear_send_flag(group
->group_source_list
);
898 if (num_sources
== 0) {
899 struct igmp_source
*source
;
900 struct in_addr any
= {.s_addr
= INADDR_ANY
};
902 source
= igmp_find_source_by_addr(group
, any
);
904 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
907 /* scan received sources (A) */
908 for (i
= 0; i
< num_sources
; ++i
) {
909 struct igmp_source
*source
;
910 struct in_addr
*src_addr
;
912 src_addr
= sources
+ i
;
914 /* lookup reported source (A) in known sources (X,Y) */
915 source
= igmp_find_source_by_addr(group
, *src_addr
);
917 /* if found, clear off DELETE flag from reported source
919 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
921 /* if not found, create source with Group Timer:
922 * (A-X-Y)=Group Timer */
923 long group_timer_msec
;
924 source
= source_new(group
, *src_addr
);
926 /* ugh, internal malloc failure, skip source */
930 zassert(!source
->t_source_timer
); /* timer == 0 */
931 group_timer_msec
= igmp_group_timer_remain_msec(group
);
932 igmp_source_timer_on(group
, source
, group_timer_msec
);
933 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
935 /* make sure source is created with DELETE flag unset */
936 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
939 /* make sure reported source has DELETE flag unset */
940 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
942 if (source
->t_source_timer
) {
943 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
944 IGMP_SOURCE_DO_SEND(source
->source_flags
);
945 ++num_sources_tosend
;
948 } /* scan received sources (A) */
951 delete all sources marked with DELETE flag:
955 source_delete_by_flag(group
->group_source_list
);
957 /* send sources marked with SEND flag: Q(G,A-Y) */
958 if (num_sources_tosend
> 0) {
959 source_query_send_by_flag(group
, num_sources_tosend
);
963 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
964 struct in_addr group_addr
, int num_sources
,
965 struct in_addr
*sources
)
967 struct interface
*ifp
= igmp
->interface
;
968 struct igmp_group
*group
;
970 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
973 /* non-existant group is created as INCLUDE {empty} */
974 group
= igmp_add_group_by_addr(igmp
, group_addr
);
979 if (group
->group_filtermode_isexcl
) {
981 toex_excl(group
, num_sources
, sources
);
984 toex_incl(group
, num_sources
, sources
);
985 zassert(group
->group_filtermode_isexcl
);
987 zassert(group
->group_filtermode_isexcl
);
989 /* Group Timer=GMI */
990 igmp_group_reset_gmi(group
);
993 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
994 struct in_addr group_addr
, int num_sources
,
995 struct in_addr
*sources
)
997 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
998 num_sources
, sources
);
1000 allow(igmp
, from
, group_addr
, num_sources
, sources
);
1004 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1006 When transmitting a group specific query, if the group timer is
1007 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1008 in the query message.
1010 static void group_retransmit_group(struct igmp_group
*group
)
1012 struct igmp_sock
*igmp
;
1013 struct pim_interface
*pim_ifp
;
1014 long lmqc
; /* Last Member Query Count */
1015 long lmqi_msec
; /* Last Member Query Interval */
1016 long lmqt_msec
; /* Last Member Query Time */
1020 igmp
= group
->group_igmp_sock
;
1021 pim_ifp
= igmp
->interface
->info
;
1023 if (pim_ifp
->igmp_version
== 3) {
1024 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1026 query_buf_size
= IGMP_V12_MSG_SIZE
;
1029 char query_buf
[query_buf_size
];
1031 lmqc
= igmp
->querier_robustness_variable
;
1032 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1033 lmqt_msec
= lmqc
* lmqi_msec
;
1036 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1038 When transmitting a group specific query, if the group timer is
1039 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1040 in the query message.
1042 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1044 if (PIM_DEBUG_IGMP_TRACE
) {
1045 char group_str
[INET_ADDRSTRLEN
];
1046 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1049 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1050 group_str
, igmp
->interface
->name
, s_flag
,
1051 group
->group_specific_query_retransmit_count
);
1055 RFC3376: 4.1.12. IP Destination Addresses for Queries
1057 Group-Specific and Group-and-Source-Specific Queries are sent with
1058 an IP destination address equal to the multicast address of
1062 igmp_send_query(pim_ifp
->igmp_version
, group
, igmp
->fd
,
1063 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
1064 0 /* num_sources_tosend */,
1065 group
->group_addr
/* dst_addr */,
1066 group
->group_addr
/* group_addr */,
1067 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1068 s_flag
, igmp
->querier_robustness_variable
,
1069 igmp
->querier_query_interval
);
1073 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1075 When building a group and source specific query for a group G, two
1076 separate query messages are sent for the group. The first one has
1077 the "Suppress Router-Side Processing" bit set and contains all the
1078 sources with retransmission state and timers greater than LMQT. The
1079 second has the "Suppress Router-Side Processing" bit clear and
1080 contains all the sources with retransmission state and timers lower
1081 or equal to LMQT. If either of the two calculated messages does not
1082 contain any sources, then its transmission is suppressed.
1084 static int group_retransmit_sources(struct igmp_group
*group
,
1085 int send_with_sflag_set
)
1087 struct igmp_sock
*igmp
;
1088 struct pim_interface
*pim_ifp
;
1089 long lmqc
; /* Last Member Query Count */
1090 long lmqi_msec
; /* Last Member Query Interval */
1091 long lmqt_msec
; /* Last Member Query Time */
1092 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1093 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1094 int query_buf1_max_sources
;
1095 int query_buf2_max_sources
;
1096 struct in_addr
*source_addr1
;
1097 struct in_addr
*source_addr2
;
1098 int num_sources_tosend1
;
1099 int num_sources_tosend2
;
1100 struct listnode
*src_node
;
1101 struct igmp_source
*src
;
1102 int num_retransmit_sources_left
= 0;
1104 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1105 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1107 igmp
= group
->group_igmp_sock
;
1108 pim_ifp
= igmp
->interface
->info
;
1110 lmqc
= igmp
->querier_robustness_variable
;
1111 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1112 lmqt_msec
= lmqc
* lmqi_msec
;
1114 /* Scan all group sources */
1115 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1117 /* Source has retransmission state? */
1118 if (src
->source_query_retransmit_count
< 1)
1121 if (--src
->source_query_retransmit_count
> 0) {
1122 ++num_retransmit_sources_left
;
1125 /* Copy source address into appropriate query buffer */
1126 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1127 *source_addr1
= src
->source_addr
;
1130 *source_addr2
= src
->source_addr
;
1135 num_sources_tosend1
=
1137 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1138 num_sources_tosend2
=
1140 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1142 if (PIM_DEBUG_IGMP_TRACE
) {
1143 char group_str
[INET_ADDRSTRLEN
];
1144 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1147 "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",
1148 group_str
, igmp
->interface
->name
, num_sources_tosend1
,
1149 num_sources_tosend2
, send_with_sflag_set
,
1150 num_retransmit_sources_left
);
1153 if (num_sources_tosend1
> 0) {
1155 Send group-and-source-specific query with s_flag set and all
1156 sources with timers greater than LMQT.
1159 if (send_with_sflag_set
) {
1161 query_buf1_max_sources
=
1162 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1164 if (num_sources_tosend1
> query_buf1_max_sources
) {
1165 char group_str
[INET_ADDRSTRLEN
];
1166 pim_inet4_dump("<group?>", group
->group_addr
,
1167 group_str
, sizeof(group_str
));
1169 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1170 __PRETTY_FUNCTION__
, group_str
,
1171 igmp
->interface
->name
,
1172 num_sources_tosend1
, sizeof(query_buf1
),
1173 query_buf1_max_sources
);
1176 RFC3376: 4.1.12. IP Destination Addresses for
1179 Group-Specific and Group-and-Source-Specific
1180 Queries are sent with
1181 an IP destination address equal to the
1182 multicast address of
1187 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1188 igmp
->interface
->name
, query_buf1
,
1189 sizeof(query_buf1
), num_sources_tosend1
,
1190 group
->group_addr
, group
->group_addr
,
1191 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1193 igmp
->querier_robustness_variable
,
1194 igmp
->querier_query_interval
);
1197 } /* send_with_sflag_set */
1200 if (num_sources_tosend2
> 0) {
1202 Send group-and-source-specific query with s_flag clear and all
1203 sources with timers lower or equal to LMQT.
1206 query_buf2_max_sources
=
1207 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1208 if (num_sources_tosend2
> query_buf2_max_sources
) {
1209 char group_str
[INET_ADDRSTRLEN
];
1210 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1213 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1214 __PRETTY_FUNCTION__
, group_str
,
1215 igmp
->interface
->name
, num_sources_tosend2
,
1216 sizeof(query_buf2
), query_buf2_max_sources
);
1219 RFC3376: 4.1.12. IP Destination Addresses for Queries
1221 Group-Specific and Group-and-Source-Specific Queries
1223 an IP destination address equal to the multicast
1229 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1230 igmp
->interface
->name
, query_buf2
,
1231 sizeof(query_buf2
), num_sources_tosend2
,
1232 group
->group_addr
, group
->group_addr
,
1233 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1235 igmp
->querier_robustness_variable
,
1236 igmp
->querier_query_interval
);
1240 return num_retransmit_sources_left
;
1243 static int igmp_group_retransmit(struct thread
*t
)
1245 struct igmp_group
*group
;
1246 int num_retransmit_sources_left
;
1247 int send_with_sflag_set
; /* boolean */
1249 group
= THREAD_ARG(t
);
1251 if (PIM_DEBUG_IGMP_TRACE
) {
1252 char group_str
[INET_ADDRSTRLEN
];
1253 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1255 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1256 group
->group_igmp_sock
->interface
->name
);
1259 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1260 if (group
->group_specific_query_retransmit_count
> 0) {
1262 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1263 group_retransmit_group(group
);
1264 --group
->group_specific_query_retransmit_count
;
1268 If a group specific query is scheduled to be transmitted at
1270 same time as a group and source specific query for the same
1272 then transmission of the group and source specific message
1274 "Suppress Router-Side Processing" bit set may be suppressed.
1276 send_with_sflag_set
= 0; /* boolean=false */
1278 send_with_sflag_set
= 1; /* boolean=true */
1281 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1282 num_retransmit_sources_left
=
1283 group_retransmit_sources(group
, send_with_sflag_set
);
1286 Keep group retransmit timer running if there is any retransmit
1289 if ((num_retransmit_sources_left
> 0)
1290 || (group
->group_specific_query_retransmit_count
> 0)) {
1291 group_retransmit_timer_on(group
);
1298 group_retransmit_timer_on:
1299 if group retransmit timer isn't running, starts it;
1300 otherwise, do nothing
1302 static void group_retransmit_timer_on(struct igmp_group
*group
)
1304 struct igmp_sock
*igmp
;
1305 struct pim_interface
*pim_ifp
;
1306 long lmqi_msec
; /* Last Member Query Interval */
1308 /* if group retransmit timer is running, do nothing */
1309 if (group
->t_group_query_retransmit_timer
) {
1313 igmp
= group
->group_igmp_sock
;
1314 pim_ifp
= igmp
->interface
->info
;
1316 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1318 if (PIM_DEBUG_IGMP_TRACE
) {
1319 char group_str
[INET_ADDRSTRLEN
];
1320 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1323 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1324 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1325 igmp
->interface
->name
);
1328 thread_add_timer_msec(master
, igmp_group_retransmit
, group
, lmqi_msec
,
1329 &group
->t_group_query_retransmit_timer
);
1332 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1334 return pim_time_timer_remain_msec(group
->t_group_timer
);
1337 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1339 return pim_time_timer_remain_msec(source
->t_source_timer
);
1343 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1345 static void group_query_send(struct igmp_group
*group
)
1347 long lmqc
; /* Last Member Query Count */
1349 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1351 /* lower group timer to lmqt */
1352 igmp_group_timer_lower_to_lmqt(group
);
1354 /* reset retransmission counter */
1355 group
->group_specific_query_retransmit_count
= lmqc
;
1357 /* immediately send group specific query (decrease retransmit counter by
1359 group_retransmit_group(group
);
1361 /* make sure group retransmit timer is running */
1362 group_retransmit_timer_on(group
);
1366 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1368 static void source_query_send_by_flag(struct igmp_group
*group
,
1369 int num_sources_tosend
)
1371 struct igmp_sock
*igmp
;
1372 struct pim_interface
*pim_ifp
;
1373 struct listnode
*src_node
;
1374 struct igmp_source
*src
;
1375 long lmqc
; /* Last Member Query Count */
1376 long lmqi_msec
; /* Last Member Query Interval */
1377 long lmqt_msec
; /* Last Member Query Time */
1379 zassert(num_sources_tosend
> 0);
1381 igmp
= group
->group_igmp_sock
;
1382 pim_ifp
= igmp
->interface
->info
;
1384 lmqc
= igmp
->querier_robustness_variable
;
1385 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1386 lmqt_msec
= lmqc
* lmqi_msec
;
1389 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1392 (...) for each of the sources in X of group G, with source timer
1395 o Set number of retransmissions for each source to [Last Member
1397 o Lower source timer to LMQT.
1399 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1400 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1401 /* source "src" in X of group G */
1402 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1403 src
->source_query_retransmit_count
= lmqc
;
1404 igmp_source_timer_lower_to_lmqt(src
);
1409 /* send group-and-source specific queries */
1410 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1412 /* make sure group retransmit timer is running */
1413 group_retransmit_timer_on(group
);
1416 static void block_excl(struct igmp_group
*group
, int num_sources
,
1417 struct in_addr
*sources
)
1419 int num_sources_tosend
= 0;
1422 /* 1. clear off SEND flag from all known sources (X,Y) */
1423 source_clear_send_flag(group
->group_source_list
);
1425 /* 2. scan received sources (A) */
1426 for (i
= 0; i
< num_sources
; ++i
) {
1427 struct igmp_source
*source
;
1428 struct in_addr
*src_addr
;
1430 src_addr
= sources
+ i
;
1432 /* lookup reported source (A) in known sources (X,Y) */
1433 source
= igmp_find_source_by_addr(group
, *src_addr
);
1435 /* 3: if not found, create source with Group Timer:
1436 * (A-X-Y)=Group Timer */
1437 long group_timer_msec
;
1438 source
= source_new(group
, *src_addr
);
1440 /* ugh, internal malloc failure, skip source */
1444 zassert(!source
->t_source_timer
); /* timer == 0 */
1445 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1446 igmp_source_timer_on(group
, source
, group_timer_msec
);
1447 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1450 if (source
->t_source_timer
) {
1451 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1452 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1453 ++num_sources_tosend
;
1457 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1458 if (num_sources_tosend
> 0) {
1459 source_query_send_by_flag(group
, num_sources_tosend
);
1463 static void block_incl(struct igmp_group
*group
, int num_sources
,
1464 struct in_addr
*sources
)
1466 int num_sources_tosend
= 0;
1469 /* 1. clear off SEND flag from all known sources (B) */
1470 source_clear_send_flag(group
->group_source_list
);
1472 /* 2. scan received sources (A) */
1473 for (i
= 0; i
< num_sources
; ++i
) {
1474 struct igmp_source
*source
;
1475 struct in_addr
*src_addr
;
1477 src_addr
= sources
+ i
;
1479 /* lookup reported source (A) in known sources (B) */
1480 source
= igmp_find_source_by_addr(group
, *src_addr
);
1482 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1483 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1484 ++num_sources_tosend
;
1488 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1489 if (num_sources_tosend
> 0) {
1490 source_query_send_by_flag(group
, num_sources_tosend
);
1494 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1495 struct in_addr group_addr
, int num_sources
,
1496 struct in_addr
*sources
)
1498 struct interface
*ifp
= igmp
->interface
;
1499 struct igmp_group
*group
;
1501 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
1504 /* non-existant group is created as INCLUDE {empty} */
1505 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1510 if (group
->group_filtermode_isexcl
) {
1512 block_excl(group
, num_sources
, sources
);
1515 block_incl(group
, num_sources
, sources
);
1519 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1521 struct igmp_sock
*igmp
;
1522 struct interface
*ifp
;
1523 struct pim_interface
*pim_ifp
;
1525 int lmqi_dsec
; /* Last Member Query Interval */
1526 int lmqc
; /* Last Member Query Count */
1527 int lmqt_msec
; /* Last Member Query Time */
1530 RFC 3376: 6.2.2. Definition of Group Timers
1532 The group timer is only used when a group is in EXCLUDE mode and
1533 it represents the time for the *filter-mode* of the group to
1534 expire and switch to INCLUDE mode.
1536 if (!group
->group_filtermode_isexcl
) {
1540 igmp
= group
->group_igmp_sock
;
1541 ifp
= igmp
->interface
;
1542 pim_ifp
= ifp
->info
;
1545 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1546 lmqc
= igmp
->querier_robustness_variable
;
1547 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1548 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1550 if (PIM_DEBUG_IGMP_TRACE
) {
1551 char group_str
[INET_ADDRSTRLEN
];
1552 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1555 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1556 __PRETTY_FUNCTION__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1560 zassert(group
->group_filtermode_isexcl
);
1562 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1565 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1567 struct igmp_group
*group
;
1568 struct igmp_sock
*igmp
;
1569 struct interface
*ifp
;
1570 struct pim_interface
*pim_ifp
;
1572 int lmqi_dsec
; /* Last Member Query Interval */
1573 int lmqc
; /* Last Member Query Count */
1574 int lmqt_msec
; /* Last Member Query Time */
1576 group
= source
->source_group
;
1577 igmp
= group
->group_igmp_sock
;
1578 ifp
= igmp
->interface
;
1579 pim_ifp
= ifp
->info
;
1582 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1583 lmqc
= igmp
->querier_robustness_variable
;
1584 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1585 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1587 if (PIM_DEBUG_IGMP_TRACE
) {
1588 char group_str
[INET_ADDRSTRLEN
];
1589 char source_str
[INET_ADDRSTRLEN
];
1590 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1592 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1593 sizeof(source_str
));
1595 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1596 __PRETTY_FUNCTION__
, group_str
, source_str
, ifname
,
1597 lmqc
, lmqi_dsec
, lmqt_msec
);
1600 igmp_source_timer_on(group
, source
, lmqt_msec
);
1603 void igmp_v3_send_query(struct igmp_group
*group
, int fd
, const char *ifname
,
1604 char *query_buf
, int query_buf_size
, int num_sources
,
1605 struct in_addr dst_addr
, struct in_addr group_addr
,
1606 int query_max_response_time_dsec
, uint8_t s_flag
,
1607 uint8_t querier_robustness_variable
,
1608 uint16_t querier_query_interval
)
1611 uint8_t max_resp_code
;
1614 struct sockaddr_in to
;
1618 zassert(num_sources
>= 0);
1620 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1621 if (msg_size
> query_buf_size
) {
1623 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1624 __FILE__
, __PRETTY_FUNCTION__
, msg_size
,
1629 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1630 zassert((s_flag
== 0) || (s_flag
== 1));
1632 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1633 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1636 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1638 If non-zero, the QRV field contains the [Robustness Variable]
1639 value used by the querier, i.e., the sender of the Query. If the
1640 querier's [Robustness Variable] exceeds 7, the maximum value of
1641 the QRV field, the QRV is set to zero.
1643 if (querier_robustness_variable
> 7) {
1644 querier_robustness_variable
= 0;
1647 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1648 query_buf
[1] = max_resp_code
;
1649 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1650 0; /* for computing checksum */
1651 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1653 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1654 query_buf
[9] = qqic
;
1655 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1658 checksum
= in_cksum(query_buf
, msg_size
);
1659 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1661 if (PIM_DEBUG_IGMP_PACKETS
) {
1662 char dst_str
[INET_ADDRSTRLEN
];
1663 char group_str
[INET_ADDRSTRLEN
];
1664 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1665 pim_inet4_dump("<group?>", group_addr
, group_str
,
1668 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1669 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1670 s_flag
, querier_robustness_variable
,
1671 querier_query_interval
, qqic
);
1674 memset(&to
, 0, sizeof(to
));
1675 to
.sin_family
= AF_INET
;
1676 to
.sin_addr
= dst_addr
;
1679 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1680 (struct sockaddr
*)&to
, tolen
);
1681 if (sent
!= (ssize_t
)msg_size
) {
1682 char dst_str
[INET_ADDRSTRLEN
];
1683 char group_str
[INET_ADDRSTRLEN
];
1684 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1685 pim_inet4_dump("<group?>", group_addr
, group_str
,
1689 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1690 dst_str
, ifname
, group_str
, msg_size
, errno
,
1691 safe_strerror(errno
));
1694 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1695 dst_str
, ifname
, group_str
, msg_size
, sent
);
1701 s_flag sanity test: s_flag must be set for general queries
1703 RFC 3376: 6.6.1. Timer Updates
1705 When a router sends or receives a query with a clear Suppress
1706 Router-Side Processing flag, it must update its timers to reflect
1707 the correct timeout values for the group or sources being queried.
1709 General queries don't trigger timer update.
1712 /* general query? */
1713 if (PIM_INADDR_IS_ANY(group_addr
)) {
1714 char dst_str
[INET_ADDRSTRLEN
];
1715 char group_str
[INET_ADDRSTRLEN
];
1716 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1718 pim_inet4_dump("<group?>", group_addr
, group_str
,
1721 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1722 __PRETTY_FUNCTION__
, dst_str
, ifname
, group_str
,
1728 void igmp_v3_recv_query(struct igmp_sock
*igmp
, const char *from_str
,
1731 struct interface
*ifp
;
1732 struct pim_interface
*pim_ifp
;
1733 struct in_addr group_addr
;
1734 uint8_t resv_s_qrv
= 0;
1739 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1740 ifp
= igmp
->interface
;
1741 pim_ifp
= ifp
->info
;
1744 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1746 * Routers adopt the QRV value from the most recently received Query
1747 * as their own [Robustness Variable] value, unless that most
1748 * recently received QRV was zero, in which case the receivers use
1749 * the default [Robustness Variable] value specified in section 8.1
1750 * or a statically configured value.
1752 resv_s_qrv
= igmp_msg
[8];
1753 qrv
= 7 & resv_s_qrv
;
1754 igmp
->querier_robustness_variable
=
1755 qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1758 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1760 * Multicast routers that are not the current querier adopt the QQI
1761 * value from the most recently received Query as their own [Query
1762 * Interval] value, unless that most recently received QQI was zero,
1763 * in which case the receiving routers use the default.
1765 if (igmp
->t_other_querier_timer
) {
1766 /* other querier present */
1770 qqi
= igmp_msg_decode8to16(qqic
);
1771 igmp
->querier_query_interval
=
1772 qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1774 if (PIM_DEBUG_IGMP_TRACE
) {
1775 char ifaddr_str
[INET_ADDRSTRLEN
];
1776 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1777 sizeof(ifaddr_str
));
1779 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1781 qqi
? "recv-non-default" : "default",
1782 igmp
->querier_query_interval
, qqic
, from_str
);
1787 * RFC 3376: 6.6.1. Timer Updates
1789 * When a router sends or receives a query with a clear Suppress
1790 * Router-Side Processing flag, it must update its timers to reflect
1791 * the correct timeout values for the group or sources being queried.
1793 * General queries don't trigger timer update.
1795 s_flag
= (1 << 3) & resv_s_qrv
;
1798 /* s_flag is clear */
1800 if (PIM_INADDR_IS_ANY(group_addr
)) {
1801 /* this is a general query */
1802 /* log that general query should have the s_flag set */
1804 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1805 from_str
, ifp
->name
);
1807 struct igmp_group
*group
;
1809 /* this is a non-general query: perform timer updates */
1811 group
= find_group_by_addr(igmp
, group_addr
);
1813 int recv_num_sources
= ntohs(*(
1816 + IGMP_V3_NUMSOURCES_OFFSET
));
1819 * RFC 3376: 6.6.1. Timer Updates
1820 * Query Q(G,A): Source Timer for sources in A
1821 * are lowered to LMQT
1822 * Query Q(G): Group Timer is lowered to LMQT
1824 if (recv_num_sources
< 1) {
1825 /* Query Q(G): Group Timer is lowered to
1828 igmp_group_timer_lower_to_lmqt(group
);
1830 /* Query Q(G,A): Source Timer for
1831 * sources in A are lowered to LMQT */
1833 /* Scan sources in query and lower their
1835 struct in_addr
*sources
=
1838 + IGMP_V3_SOURCES_OFFSET
);
1839 for (i
= 0; i
< recv_num_sources
; ++i
) {
1840 struct in_addr src_addr
;
1841 struct igmp_source
*src
;
1842 memcpy(&src_addr
, sources
+ i
,
1843 sizeof(struct in_addr
));
1844 src
= igmp_find_source_by_addr(
1847 igmp_source_timer_lower_to_lmqt(
1853 char group_str
[INET_ADDRSTRLEN
];
1854 pim_inet4_dump("<group?>", group_addr
,
1855 group_str
, sizeof(group_str
));
1857 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1858 from_str
, ifp
->name
, group_str
);
1861 } /* s_flag is clear: timer updates */
1864 int igmp_v3_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
1865 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1867 uint16_t recv_checksum
;
1870 uint8_t *group_record
;
1871 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1872 struct interface
*ifp
= igmp
->interface
;
1875 struct pim_interface
*pim_ifp
;
1877 if (igmp
->mtrace_only
)
1880 pim_ifp
= igmp
->interface
->info
;
1882 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1884 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1885 from_str
, ifp
->name
, igmp_msg_len
,
1886 IGMP_V3_MSG_MIN_SIZE
);
1890 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1892 /* for computing checksum */
1893 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1895 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1896 if (checksum
!= recv_checksum
) {
1898 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1899 from_str
, ifp
->name
, recv_checksum
, checksum
);
1903 /* Collecting IGMP Rx stats */
1904 igmp
->rx_stats
.report_v3
++;
1907 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1908 if (num_groups
< 1) {
1910 "Recv IGMP report v3 from %s on %s: missing group records",
1911 from_str
, ifp
->name
);
1915 if (PIM_DEBUG_IGMP_PACKETS
) {
1917 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1918 from_str
, ifp
->name
, igmp_msg_len
, checksum
,
1922 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1925 for (i
= 0; i
< num_groups
; ++i
) {
1926 struct in_addr rec_group
;
1931 int rec_num_sources
;
1935 bool filtered
= false;
1937 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1940 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1941 from_str
, ifp
->name
);
1945 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1947 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1948 rec_num_sources
= ntohs(*(
1949 uint16_t *)(group_record
1950 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1953 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1954 sizeof(struct in_addr
));
1956 if (PIM_DEBUG_IGMP_PACKETS
) {
1958 "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1959 from_str
, ifp
->name
, i
, rec_type
,
1960 rec_auxdatalen
, rec_num_sources
,
1961 inet_ntoa(rec_group
));
1966 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1968 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1970 if ((src
+ 4) > report_pastend
) {
1972 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1973 from_str
, ifp
->name
);
1977 if (PIM_DEBUG_IGMP_PACKETS
) {
1980 if (!inet_ntop(AF_INET
, src
, src_str
,
1982 sprintf(src_str
, "<source?>");
1985 "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1986 from_str
, ifp
->name
, i
,
1987 inet_ntoa(rec_group
), src_str
);
1989 } /* for (sources) */
1992 lncb
.family
= AF_INET
;
1993 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1994 lncb
.prefixlen
= 24;
1997 g
.u
.prefix4
= rec_group
;
2000 /* determine filtering status for group */
2001 filtered
= pim_is_group_filtered(ifp
->info
, &rec_group
);
2003 if (PIM_DEBUG_IGMP_PACKETS
&& filtered
)
2005 "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
2006 inet_ntoa(rec_group
), from_str
, ifp
->name
,
2007 pim_ifp
->boundary_oil_plist
);
2010 * If we receive a igmp report with the group in 224.0.0.0/24
2011 * then we should ignore it
2013 if (prefix_match(&lncb
, &g
))
2016 if (!local_ncb
&& !filtered
)
2018 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
2019 igmpv3_report_isin(igmp
, from
, rec_group
,
2021 (struct in_addr
*)sources
);
2023 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
2025 igmp
, from
, rec_group
, rec_num_sources
,
2026 (struct in_addr
*)sources
, 0);
2028 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
2029 igmpv3_report_toin(igmp
, from
, rec_group
,
2031 (struct in_addr
*)sources
);
2033 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
2034 igmpv3_report_toex(igmp
, from
, rec_group
,
2036 (struct in_addr
*)sources
);
2038 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2039 igmpv3_report_allow(igmp
, from
, rec_group
,
2041 (struct in_addr
*)sources
);
2043 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2044 igmpv3_report_block(igmp
, from
, rec_group
,
2046 (struct in_addr
*)sources
);
2050 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2051 from_str
, ifp
->name
, rec_type
);
2055 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2058 } /* for (group records) */