3 * Copyright (C) 2008 Everton da Silva Marques
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "lib_errors.h"
27 #include "pim_iface.h"
29 #include "pim_igmpv3.h"
33 #include "pim_zebra.h"
36 static void group_retransmit_timer_on(struct igmp_group
*group
);
37 static long igmp_group_timer_remain_msec(struct igmp_group
*group
);
38 static long igmp_source_timer_remain_msec(struct igmp_source
*source
);
39 static void group_query_send(struct igmp_group
*group
);
40 static void source_query_send_by_flag(struct igmp_group
*group
,
41 int num_sources_tosend
);
43 static void on_trace(const char *label
, struct interface
*ifp
,
44 struct in_addr from
, struct in_addr group_addr
,
45 int num_sources
, struct in_addr
*sources
)
47 if (PIM_DEBUG_IGMP_TRACE
) {
48 char from_str
[INET_ADDRSTRLEN
];
49 char group_str
[INET_ADDRSTRLEN
];
51 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
52 pim_inet4_dump("<group?>", group_addr
, group_str
,
55 zlog_debug("%s: from %s on %s: group=%s sources=%d", label
,
56 from_str
, ifp
->name
, group_str
, num_sources
);
60 void igmp_group_reset_gmi(struct igmp_group
*group
)
62 long group_membership_interval_msec
;
63 struct pim_interface
*pim_ifp
;
64 struct igmp_sock
*igmp
;
65 struct interface
*ifp
;
67 igmp
= group
->group_igmp_sock
;
68 ifp
= igmp
->interface
;
72 RFC 3376: 8.4. Group Membership Interval
74 The Group Membership Interval is the amount of time that must pass
75 before a multicast router decides there are no more members of a
76 group or a particular source on a network.
78 This value MUST be ((the Robustness Variable) times (the Query
79 Interval)) plus (one Query Response Interval).
81 group_membership_interval_msec = querier_robustness_variable *
82 (1000 * querier_query_interval) +
83 100 * query_response_interval_dsec;
85 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
86 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
87 pim_ifp
->igmp_query_max_response_time_dsec
);
89 if (PIM_DEBUG_IGMP_TRACE
) {
90 char group_str
[INET_ADDRSTRLEN
];
91 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
94 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
95 group_str
, group_membership_interval_msec
/ 1000,
96 group_membership_interval_msec
% 1000, ifp
->name
);
100 RFC 3376: 6.2.2. Definition of Group Timers
102 The group timer is only used when a group is in EXCLUDE mode and
103 it represents the time for the *filter-mode* of the group to
104 expire and switch to INCLUDE mode.
106 zassert(group
->group_filtermode_isexcl
);
108 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
111 static int igmp_source_timer(struct thread
*t
)
113 struct igmp_source
*source
;
114 struct igmp_group
*group
;
116 source
= THREAD_ARG(t
);
118 group
= source
->source_group
;
120 if (PIM_DEBUG_IGMP_TRACE
) {
121 char group_str
[INET_ADDRSTRLEN
];
122 char source_str
[INET_ADDRSTRLEN
];
123 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
125 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
128 "%s: Source timer expired for group %s source %s on %s",
129 __PRETTY_FUNCTION__
, group_str
, source_str
,
130 group
->group_igmp_sock
->interface
->name
);
134 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
137 Filter-Mode Source Timer Value Action
138 ----------- ------------------ ------
139 INCLUDE TIMER == 0 Suggest to stop forwarding
140 traffic from source and
141 remove source record. If
142 there are no more source
143 records for the group, delete
146 EXCLUDE TIMER == 0 Suggest to not forward
148 (DO NOT remove record)
150 Source timer switched from (T > 0) to (T == 0): disable forwarding.
153 if (group
->group_filtermode_isexcl
) {
156 igmp_source_forward_stop(source
);
160 /* igmp_source_delete() will stop forwarding source */
161 igmp_source_delete(source
);
164 If there are no more source records for the group, delete
168 if (!listcount(group
->group_source_list
)) {
169 igmp_group_delete_empty_include(group
);
176 static void source_timer_off(struct igmp_group
*group
,
177 struct igmp_source
*source
)
179 if (!source
->t_source_timer
)
182 if (PIM_DEBUG_IGMP_TRACE
) {
183 char group_str
[INET_ADDRSTRLEN
];
184 char source_str
[INET_ADDRSTRLEN
];
185 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
187 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
190 "Cancelling TIMER event for group %s source %s on %s",
191 group_str
, source_str
,
192 group
->group_igmp_sock
->interface
->name
);
195 THREAD_OFF(source
->t_source_timer
);
198 static void igmp_source_timer_on(struct igmp_group
*group
,
199 struct igmp_source
*source
, long interval_msec
)
201 source_timer_off(group
, source
);
202 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
204 if (PIM_DEBUG_IGMP_EVENTS
) {
205 char group_str
[INET_ADDRSTRLEN
];
206 char source_str
[INET_ADDRSTRLEN
];
207 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
209 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
212 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
213 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
214 source_str
, group
->group_igmp_sock
->interface
->name
);
217 thread_add_timer_msec(router
->master
, igmp_source_timer
, source
,
218 interval_msec
, &source
->t_source_timer
);
221 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
223 Source timer switched from (T == 0) to (T > 0): enable forwarding.
225 igmp_source_forward_start(pim_ifp
->pim
, source
);
228 void igmp_source_reset_gmi(struct igmp_sock
*igmp
, struct igmp_group
*group
,
229 struct igmp_source
*source
)
231 long group_membership_interval_msec
;
232 struct pim_interface
*pim_ifp
;
233 struct interface
*ifp
;
235 ifp
= igmp
->interface
;
238 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
239 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
240 pim_ifp
->igmp_query_max_response_time_dsec
);
242 if (PIM_DEBUG_IGMP_TRACE
) {
243 char group_str
[INET_ADDRSTRLEN
];
244 char source_str
[INET_ADDRSTRLEN
];
246 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
248 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
252 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
253 source_str
, group_membership_interval_msec
/ 1000,
254 group_membership_interval_msec
% 1000, group_str
,
258 igmp_source_timer_on(group
, source
, group_membership_interval_msec
);
261 static void source_mark_delete_flag(struct igmp_group
*group
)
263 struct listnode
*src_node
;
264 struct igmp_source
*src
;
266 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
267 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
271 static void source_mark_send_flag(struct igmp_group
*group
)
273 struct listnode
*src_node
;
274 struct igmp_source
*src
;
276 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
277 IGMP_SOURCE_DO_SEND(src
->source_flags
);
281 static int source_mark_send_flag_by_timer(struct igmp_group
*group
)
283 struct listnode
*src_node
;
284 struct igmp_source
*src
;
285 int num_marked_sources
= 0;
287 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
288 /* Is source timer running? */
289 if (src
->t_source_timer
) {
290 IGMP_SOURCE_DO_SEND(src
->source_flags
);
291 ++num_marked_sources
;
293 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
297 return num_marked_sources
;
300 static void source_clear_send_flag(struct list
*source_list
)
302 struct listnode
*src_node
;
303 struct igmp_source
*src
;
305 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
306 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
311 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
313 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
315 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
317 zassert(group
->group_filtermode_isexcl
);
319 if (listcount(group
->group_source_list
) < 1) {
320 igmp_anysource_forward_start(pim_ifp
->pim
, group
);
324 void igmp_source_free(struct igmp_source
*source
)
326 /* make sure there is no source timer running */
327 zassert(!source
->t_source_timer
);
329 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
332 static void source_channel_oil_detach(struct igmp_source
*source
)
334 if (source
->source_channel_oil
) {
335 pim_channel_oil_del(source
->source_channel_oil
,
336 __PRETTY_FUNCTION__
);
337 source
->source_channel_oil
= NULL
;
342 igmp_source_delete: stop fowarding, and delete the source
343 igmp_source_forward_stop: stop fowarding, but keep the source
345 void igmp_source_delete(struct igmp_source
*source
)
347 struct igmp_group
*group
;
350 group
= source
->source_group
;
352 if (PIM_DEBUG_IGMP_TRACE
) {
353 char group_str
[INET_ADDRSTRLEN
];
354 char source_str
[INET_ADDRSTRLEN
];
355 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
357 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
360 "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
361 source_str
, group_str
, group
->group_igmp_sock
->fd
,
362 group
->group_igmp_sock
->interface
->name
,
363 source
->source_channel_oil
364 ? source
->source_channel_oil
->oil_ref_count
368 source_timer_off(group
, source
);
369 igmp_source_forward_stop(source
);
371 /* sanity check that forwarding has been disabled */
372 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
373 char group_str
[INET_ADDRSTRLEN
];
374 char source_str
[INET_ADDRSTRLEN
];
375 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
377 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
380 "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
381 __PRETTY_FUNCTION__
, source_str
, group_str
,
382 group
->group_igmp_sock
->fd
,
383 group
->group_igmp_sock
->interface
->name
);
387 source_channel_oil_detach(source
);
390 notice that listnode_delete() can't be moved
391 into igmp_source_free() because the later is
392 called by list_delete_all_node()
394 listnode_delete(group
->group_source_list
, source
);
396 src
.s_addr
= source
->source_addr
.s_addr
;
397 igmp_source_free(source
);
399 /* Group source list is empty and current source is * then
400 *,G group going away so do not trigger start */
401 if (group
->group_filtermode_isexcl
402 && (listcount(group
->group_source_list
) != 0)
403 && src
.s_addr
!= INADDR_ANY
) {
404 group_exclude_fwd_anysrc_ifempty(group
);
408 static void source_delete_by_flag(struct list
*source_list
)
410 struct listnode
*src_node
;
411 struct listnode
*src_nextnode
;
412 struct igmp_source
*src
;
414 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
415 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
416 igmp_source_delete(src
);
419 void igmp_source_delete_expired(struct list
*source_list
)
421 struct listnode
*src_node
;
422 struct listnode
*src_nextnode
;
423 struct igmp_source
*src
;
425 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
426 if (!src
->t_source_timer
)
427 igmp_source_delete(src
);
430 struct igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
431 struct in_addr src_addr
)
433 struct listnode
*src_node
;
434 struct igmp_source
*src
;
436 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
437 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
443 struct igmp_source
*source_new(struct igmp_group
*group
,
444 struct in_addr src_addr
)
446 struct igmp_source
*src
;
448 if (PIM_DEBUG_IGMP_TRACE
) {
449 char group_str
[INET_ADDRSTRLEN
];
450 char source_str
[INET_ADDRSTRLEN
];
451 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
453 pim_inet4_dump("<source?>", src_addr
, source_str
,
456 "Creating new IGMP source %s for group %s on socket %d interface %s",
457 source_str
, group_str
, group
->group_igmp_sock
->fd
,
458 group
->group_igmp_sock
->interface
->name
);
461 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
463 src
->t_source_timer
= NULL
;
464 src
->source_group
= group
; /* back pointer */
465 src
->source_addr
= src_addr
;
466 src
->source_creation
= pim_time_monotonic_sec();
467 src
->source_flags
= 0;
468 src
->source_query_retransmit_count
= 0;
469 src
->source_channel_oil
= NULL
;
471 listnode_add(group
->group_source_list
, src
);
473 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
474 igmp_anysource_forward_stop(group
);
479 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
480 struct igmp_group
*group
,
481 struct in_addr src_addr
)
483 struct igmp_source
*src
;
485 src
= igmp_find_source_by_addr(group
, src_addr
);
490 src
= source_new(group
, src_addr
);
495 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
496 struct in_addr group_addr
, int num_sources
,
497 struct in_addr
*sources
)
499 struct igmp_source
*source
;
500 struct igmp_group
*group
;
503 /* non-existant group is created as INCLUDE {empty} */
504 group
= igmp_add_group_by_addr(igmp
, group_addr
);
509 /* scan received sources */
510 for (i
= 0; i
< num_sources
; ++i
) {
511 struct in_addr
*src_addr
;
513 src_addr
= sources
+ i
;
515 source
= add_source_by_addr(igmp
, group
, *src_addr
);
521 RFC 3376: 6.4.1. Reception of Current-State Records
523 When receiving IS_IN reports for groups in EXCLUDE mode is
524 sources should be moved from set with (timers = 0) to set with
527 igmp_source_reset_gmi() below, resetting the source timers to
528 GMI, accomplishes this.
530 igmp_source_reset_gmi(igmp
, group
, source
);
532 } /* scan received sources */
534 if ((num_sources
== 0) && (group
->group_filtermode_isexcl
)
535 && (listcount(group
->group_source_list
) == 1)) {
536 struct in_addr star
= {.s_addr
= INADDR_ANY
};
538 source
= igmp_find_source_by_addr(group
, star
);
540 igmp_source_reset_gmi(igmp
, group
, source
);
544 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
545 struct in_addr group_addr
, int num_sources
,
546 struct in_addr
*sources
)
548 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
549 num_sources
, sources
);
551 allow(igmp
, from
, group_addr
, num_sources
, sources
);
554 static void isex_excl(struct igmp_group
*group
, int num_sources
,
555 struct in_addr
*sources
)
557 struct igmp_source
*source
;
561 zassert(group
->group_filtermode_isexcl
);
563 /* E.1: set deletion flag for known sources (X,Y) */
564 source_mark_delete_flag(group
);
566 /* scan received sources (A) */
567 for (i
= 0; i
< num_sources
; ++i
) {
568 struct in_addr
*src_addr
;
570 src_addr
= sources
+ i
;
572 /* E.2: lookup reported source from (A) in (X,Y) */
573 source
= igmp_find_source_by_addr(group
, *src_addr
);
575 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
576 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
578 /* E.4: if not found, create source with timer=GMI:
580 source
= source_new(group
, *src_addr
);
581 zassert(!source
->t_source_timer
); /* timer == 0 */
582 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
584 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
587 } /* scan received sources */
590 * If we are in isexcl mode and num_sources == 0
591 * than that means we have a *,g entry that
592 * needs to be handled
594 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
595 struct in_addr star
= {.s_addr
= INADDR_ANY
};
596 source
= igmp_find_source_by_addr(group
, star
);
598 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
599 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
604 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
605 source_delete_by_flag(group
->group_source_list
);
608 static void isex_incl(struct igmp_group
*group
, int num_sources
,
609 struct in_addr
*sources
)
614 zassert(!group
->group_filtermode_isexcl
);
616 /* I.1: set deletion flag for known sources (A) */
617 source_mark_delete_flag(group
);
619 /* scan received sources (B) */
620 for (i
= 0; i
< num_sources
; ++i
) {
621 struct igmp_source
*source
;
622 struct in_addr
*src_addr
;
624 src_addr
= sources
+ i
;
626 /* I.2: lookup reported source (B) */
627 source
= igmp_find_source_by_addr(group
, *src_addr
);
629 /* I.3: if found, clear deletion flag (A*B) */
630 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
632 /* I.4: if not found, create source with timer=0 (B-A)
634 source
= source_new(group
, *src_addr
);
635 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
638 } /* scan received sources */
640 /* I.5: delete all sources marked with deletion flag (A-B) */
641 source_delete_by_flag(group
->group_source_list
);
643 group
->group_filtermode_isexcl
= 1; /* boolean=true */
645 zassert(group
->group_filtermode_isexcl
);
647 group_exclude_fwd_anysrc_ifempty(group
);
650 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
651 struct in_addr group_addr
, int num_sources
,
652 struct in_addr
*sources
, int from_igmp_v2_report
)
654 struct interface
*ifp
= igmp
->interface
;
655 struct igmp_group
*group
;
657 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
660 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
663 /* non-existant group is created as INCLUDE {empty} */
664 group
= igmp_add_group_by_addr(igmp
, group_addr
);
669 /* So we can display how we learned the group in our show command output
671 if (from_igmp_v2_report
)
672 group
->igmp_version
= 2;
674 if (group
->group_filtermode_isexcl
) {
676 isex_excl(group
, num_sources
, sources
);
679 isex_incl(group
, num_sources
, sources
);
680 zassert(group
->group_filtermode_isexcl
);
683 zassert(group
->group_filtermode_isexcl
);
685 igmp_group_reset_gmi(group
);
688 static void toin_incl(struct igmp_group
*group
, int num_sources
,
689 struct in_addr
*sources
)
691 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
692 int num_sources_tosend
= listcount(group
->group_source_list
);
695 /* Set SEND flag for all known sources (A) */
696 source_mark_send_flag(group
);
698 /* Scan received sources (B) */
699 for (i
= 0; i
< num_sources
; ++i
) {
700 struct igmp_source
*source
;
701 struct in_addr
*src_addr
;
703 src_addr
= sources
+ i
;
705 /* Lookup reported source (B) */
706 source
= igmp_find_source_by_addr(group
, *src_addr
);
708 /* If found, clear SEND flag (A*B) */
709 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
710 --num_sources_tosend
;
712 /* If not found, create new source */
713 source
= source_new(group
, *src_addr
);
717 igmp_source_reset_gmi(igmp
, group
, source
);
720 /* Send sources marked with SEND flag: Q(G,A-B) */
721 if (num_sources_tosend
> 0) {
722 source_query_send_by_flag(group
, num_sources_tosend
);
726 static void toin_excl(struct igmp_group
*group
, int num_sources
,
727 struct in_addr
*sources
)
729 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
730 int num_sources_tosend
;
733 /* Set SEND flag for X (sources with timer > 0) */
734 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
736 /* Scan received sources (A) */
737 for (i
= 0; i
< num_sources
; ++i
) {
738 struct igmp_source
*source
;
739 struct in_addr
*src_addr
;
741 src_addr
= sources
+ i
;
743 /* Lookup reported source (A) */
744 source
= igmp_find_source_by_addr(group
, *src_addr
);
746 if (source
->t_source_timer
) {
747 /* If found and timer running, clear SEND flag
749 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
750 --num_sources_tosend
;
753 /* If not found, create new source */
754 source
= source_new(group
, *src_addr
);
758 igmp_source_reset_gmi(igmp
, group
, source
);
761 /* Send sources marked with SEND flag: Q(G,X-A) */
762 if (num_sources_tosend
> 0) {
763 source_query_send_by_flag(group
, num_sources_tosend
);
767 group_query_send(group
);
770 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
771 struct in_addr group_addr
, int num_sources
,
772 struct in_addr
*sources
)
774 struct interface
*ifp
= igmp
->interface
;
775 struct igmp_group
*group
;
777 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
781 * If the requested filter mode is INCLUDE *and* the requested source
782 * list is empty, then the entry corresponding to the requested
783 * interface and multicast address is deleted if present. If no such
784 * entry is present, the request is ignored.
787 /* non-existant group is created as INCLUDE {empty} */
788 group
= igmp_add_group_by_addr(igmp
, group_addr
);
793 group
= find_group_by_addr(igmp
, group_addr
);
798 if (group
->group_filtermode_isexcl
) {
800 toin_excl(group
, num_sources
, sources
);
803 toin_incl(group
, num_sources
, sources
);
807 static void toex_incl(struct igmp_group
*group
, int num_sources
,
808 struct in_addr
*sources
)
810 int num_sources_tosend
= 0;
813 zassert(!group
->group_filtermode_isexcl
);
815 /* Set DELETE flag for all known sources (A) */
816 source_mark_delete_flag(group
);
818 /* Clear off SEND flag from all known sources (A) */
819 source_clear_send_flag(group
->group_source_list
);
821 /* Scan received sources (B) */
822 for (i
= 0; i
< num_sources
; ++i
) {
823 struct igmp_source
*source
;
824 struct in_addr
*src_addr
;
826 src_addr
= sources
+ i
;
828 /* Lookup reported source (B) */
829 source
= igmp_find_source_by_addr(group
, *src_addr
);
831 /* If found, clear deletion flag: (A*B) */
832 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
833 /* and set SEND flag (A*B) */
834 IGMP_SOURCE_DO_SEND(source
->source_flags
);
835 ++num_sources_tosend
;
837 /* If source not found, create source with timer=0:
839 source
= source_new(group
, *src_addr
);
840 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
843 } /* Scan received sources (B) */
845 group
->group_filtermode_isexcl
= 1; /* boolean=true */
847 /* Delete all sources marked with DELETE flag (A-B) */
848 source_delete_by_flag(group
->group_source_list
);
850 /* Send sources marked with SEND flag: Q(G,A*B) */
851 if (num_sources_tosend
> 0) {
852 source_query_send_by_flag(group
, num_sources_tosend
);
855 zassert(group
->group_filtermode_isexcl
);
857 group_exclude_fwd_anysrc_ifempty(group
);
860 static void toex_excl(struct igmp_group
*group
, int num_sources
,
861 struct in_addr
*sources
)
863 int num_sources_tosend
= 0;
866 /* set DELETE flag for all known sources (X,Y) */
867 source_mark_delete_flag(group
);
869 /* clear off SEND flag from all known sources (X,Y) */
870 source_clear_send_flag(group
->group_source_list
);
872 if (num_sources
== 0) {
873 struct igmp_source
*source
;
874 struct in_addr any
= {.s_addr
= INADDR_ANY
};
876 source
= igmp_find_source_by_addr(group
, any
);
878 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
881 /* scan received sources (A) */
882 for (i
= 0; i
< num_sources
; ++i
) {
883 struct igmp_source
*source
;
884 struct in_addr
*src_addr
;
886 src_addr
= sources
+ i
;
888 /* lookup reported source (A) in known sources (X,Y) */
889 source
= igmp_find_source_by_addr(group
, *src_addr
);
891 /* if found, clear off DELETE flag from reported source
893 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
895 /* if not found, create source with Group Timer:
896 * (A-X-Y)=Group Timer */
897 long group_timer_msec
;
898 source
= source_new(group
, *src_addr
);
900 zassert(!source
->t_source_timer
); /* timer == 0 */
901 group_timer_msec
= igmp_group_timer_remain_msec(group
);
902 igmp_source_timer_on(group
, source
, group_timer_msec
);
903 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
905 /* make sure source is created with DELETE flag unset */
906 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
909 /* make sure reported source has DELETE flag unset */
910 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
912 if (source
->t_source_timer
) {
913 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
914 IGMP_SOURCE_DO_SEND(source
->source_flags
);
915 ++num_sources_tosend
;
918 } /* scan received sources (A) */
921 delete all sources marked with DELETE flag:
925 source_delete_by_flag(group
->group_source_list
);
927 /* send sources marked with SEND flag: Q(G,A-Y) */
928 if (num_sources_tosend
> 0) {
929 source_query_send_by_flag(group
, num_sources_tosend
);
933 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
934 struct in_addr group_addr
, int num_sources
,
935 struct in_addr
*sources
)
937 struct interface
*ifp
= igmp
->interface
;
938 struct igmp_group
*group
;
940 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
943 /* non-existant group is created as INCLUDE {empty} */
944 group
= igmp_add_group_by_addr(igmp
, group_addr
);
949 if (group
->group_filtermode_isexcl
) {
951 toex_excl(group
, num_sources
, sources
);
954 toex_incl(group
, num_sources
, sources
);
955 zassert(group
->group_filtermode_isexcl
);
957 zassert(group
->group_filtermode_isexcl
);
959 /* Group Timer=GMI */
960 igmp_group_reset_gmi(group
);
963 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
964 struct in_addr group_addr
, int num_sources
,
965 struct in_addr
*sources
)
967 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
968 num_sources
, sources
);
970 allow(igmp
, from
, group_addr
, num_sources
, sources
);
974 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
976 When transmitting a group specific query, if the group timer is
977 larger than LMQT, the "Suppress Router-Side Processing" bit is set
978 in the query message.
980 static void group_retransmit_group(struct igmp_group
*group
)
982 struct igmp_sock
*igmp
;
983 struct pim_interface
*pim_ifp
;
984 long lmqc
; /* Last Member Query Count */
985 long lmqi_msec
; /* Last Member Query Interval */
986 long lmqt_msec
; /* Last Member Query Time */
990 igmp
= group
->group_igmp_sock
;
991 pim_ifp
= igmp
->interface
->info
;
993 if (pim_ifp
->igmp_version
== 3) {
994 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
996 query_buf_size
= IGMP_V12_MSG_SIZE
;
999 char query_buf
[query_buf_size
];
1001 lmqc
= pim_ifp
->igmp_last_member_query_count
;
1002 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1003 lmqt_msec
= lmqc
* lmqi_msec
;
1006 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1008 When transmitting a group specific query, if the group timer is
1009 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1010 in the query message.
1012 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1014 if (PIM_DEBUG_IGMP_TRACE
) {
1015 char group_str
[INET_ADDRSTRLEN
];
1016 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1019 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1020 group_str
, igmp
->interface
->name
, s_flag
,
1021 group
->group_specific_query_retransmit_count
);
1025 RFC3376: 4.1.12. IP Destination Addresses for Queries
1027 Group-Specific and Group-and-Source-Specific Queries are sent with
1028 an IP destination address equal to the multicast address of
1032 igmp_send_query(pim_ifp
->igmp_version
, group
, igmp
->fd
,
1033 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
1034 0 /* num_sources_tosend */,
1035 group
->group_addr
/* dst_addr */,
1036 group
->group_addr
/* group_addr */,
1037 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1038 s_flag
, igmp
->querier_robustness_variable
,
1039 igmp
->querier_query_interval
);
1043 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1045 When building a group and source specific query for a group G, two
1046 separate query messages are sent for the group. The first one has
1047 the "Suppress Router-Side Processing" bit set and contains all the
1048 sources with retransmission state and timers greater than LMQT. The
1049 second has the "Suppress Router-Side Processing" bit clear and
1050 contains all the sources with retransmission state and timers lower
1051 or equal to LMQT. If either of the two calculated messages does not
1052 contain any sources, then its transmission is suppressed.
1054 static int group_retransmit_sources(struct igmp_group
*group
,
1055 int send_with_sflag_set
)
1057 struct igmp_sock
*igmp
;
1058 struct pim_interface
*pim_ifp
;
1059 long lmqc
; /* Last Member Query Count */
1060 long lmqi_msec
; /* Last Member Query Interval */
1061 long lmqt_msec
; /* Last Member Query Time */
1062 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1063 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1064 int query_buf1_max_sources
;
1065 int query_buf2_max_sources
;
1066 struct in_addr
*source_addr1
;
1067 struct in_addr
*source_addr2
;
1068 int num_sources_tosend1
;
1069 int num_sources_tosend2
;
1070 struct listnode
*src_node
;
1071 struct igmp_source
*src
;
1072 int num_retransmit_sources_left
= 0;
1074 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1075 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1077 igmp
= group
->group_igmp_sock
;
1078 pim_ifp
= igmp
->interface
->info
;
1080 lmqc
= pim_ifp
->igmp_last_member_query_count
;
1081 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1082 lmqt_msec
= lmqc
* lmqi_msec
;
1084 /* Scan all group sources */
1085 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1087 /* Source has retransmission state? */
1088 if (src
->source_query_retransmit_count
< 1)
1091 if (--src
->source_query_retransmit_count
> 0) {
1092 ++num_retransmit_sources_left
;
1095 /* Copy source address into appropriate query buffer */
1096 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1097 *source_addr1
= src
->source_addr
;
1100 *source_addr2
= src
->source_addr
;
1105 num_sources_tosend1
=
1107 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1108 num_sources_tosend2
=
1110 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1112 if (PIM_DEBUG_IGMP_TRACE
) {
1113 char group_str
[INET_ADDRSTRLEN
];
1114 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1117 "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",
1118 group_str
, igmp
->interface
->name
, num_sources_tosend1
,
1119 num_sources_tosend2
, send_with_sflag_set
,
1120 num_retransmit_sources_left
);
1123 if (num_sources_tosend1
> 0) {
1125 Send group-and-source-specific query with s_flag set and all
1126 sources with timers greater than LMQT.
1129 if (send_with_sflag_set
) {
1131 query_buf1_max_sources
=
1132 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1134 if (num_sources_tosend1
> query_buf1_max_sources
) {
1135 char group_str
[INET_ADDRSTRLEN
];
1136 pim_inet4_dump("<group?>", group
->group_addr
,
1137 group_str
, sizeof(group_str
));
1139 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1140 __PRETTY_FUNCTION__
, group_str
,
1141 igmp
->interface
->name
,
1142 num_sources_tosend1
, sizeof(query_buf1
),
1143 query_buf1_max_sources
);
1146 RFC3376: 4.1.12. IP Destination Addresses for
1149 Group-Specific and Group-and-Source-Specific
1150 Queries are sent with
1151 an IP destination address equal to the
1152 multicast address of
1157 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1158 igmp
->interface
->name
, query_buf1
,
1159 sizeof(query_buf1
), num_sources_tosend1
,
1160 group
->group_addr
, group
->group_addr
,
1161 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1163 igmp
->querier_robustness_variable
,
1164 igmp
->querier_query_interval
);
1167 } /* send_with_sflag_set */
1170 if (num_sources_tosend2
> 0) {
1172 Send group-and-source-specific query with s_flag clear and all
1173 sources with timers lower or equal to LMQT.
1176 query_buf2_max_sources
=
1177 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1178 if (num_sources_tosend2
> query_buf2_max_sources
) {
1179 char group_str
[INET_ADDRSTRLEN
];
1180 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1183 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1184 __PRETTY_FUNCTION__
, group_str
,
1185 igmp
->interface
->name
, num_sources_tosend2
,
1186 sizeof(query_buf2
), query_buf2_max_sources
);
1189 RFC3376: 4.1.12. IP Destination Addresses for Queries
1191 Group-Specific and Group-and-Source-Specific Queries
1193 an IP destination address equal to the multicast
1199 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1200 igmp
->interface
->name
, query_buf2
,
1201 sizeof(query_buf2
), num_sources_tosend2
,
1202 group
->group_addr
, group
->group_addr
,
1203 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1205 igmp
->querier_robustness_variable
,
1206 igmp
->querier_query_interval
);
1210 return num_retransmit_sources_left
;
1213 static int igmp_group_retransmit(struct thread
*t
)
1215 struct igmp_group
*group
;
1216 int num_retransmit_sources_left
;
1217 int send_with_sflag_set
; /* boolean */
1219 group
= THREAD_ARG(t
);
1221 if (PIM_DEBUG_IGMP_TRACE
) {
1222 char group_str
[INET_ADDRSTRLEN
];
1223 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1225 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1226 group
->group_igmp_sock
->interface
->name
);
1229 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1230 if (group
->group_specific_query_retransmit_count
> 0) {
1232 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1233 group_retransmit_group(group
);
1234 --group
->group_specific_query_retransmit_count
;
1238 If a group specific query is scheduled to be transmitted at
1240 same time as a group and source specific query for the same
1242 then transmission of the group and source specific message
1244 "Suppress Router-Side Processing" bit set may be suppressed.
1246 send_with_sflag_set
= 0; /* boolean=false */
1248 send_with_sflag_set
= 1; /* boolean=true */
1251 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1252 num_retransmit_sources_left
=
1253 group_retransmit_sources(group
, send_with_sflag_set
);
1256 Keep group retransmit timer running if there is any retransmit
1259 if ((num_retransmit_sources_left
> 0)
1260 || (group
->group_specific_query_retransmit_count
> 0)) {
1261 group_retransmit_timer_on(group
);
1268 group_retransmit_timer_on:
1269 if group retransmit timer isn't running, starts it;
1270 otherwise, do nothing
1272 static void group_retransmit_timer_on(struct igmp_group
*group
)
1274 struct igmp_sock
*igmp
;
1275 struct pim_interface
*pim_ifp
;
1276 long lmqi_msec
; /* Last Member Query Interval */
1278 /* if group retransmit timer is running, do nothing */
1279 if (group
->t_group_query_retransmit_timer
) {
1283 igmp
= group
->group_igmp_sock
;
1284 pim_ifp
= igmp
->interface
->info
;
1286 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1288 if (PIM_DEBUG_IGMP_TRACE
) {
1289 char group_str
[INET_ADDRSTRLEN
];
1290 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1293 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1294 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1295 igmp
->interface
->name
);
1298 thread_add_timer_msec(router
->master
, igmp_group_retransmit
, group
,
1300 &group
->t_group_query_retransmit_timer
);
1303 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1305 return pim_time_timer_remain_msec(group
->t_group_timer
);
1308 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1310 return pim_time_timer_remain_msec(source
->t_source_timer
);
1314 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1316 static void group_query_send(struct igmp_group
*group
)
1318 struct pim_interface
*pim_ifp
;
1319 struct igmp_sock
*igmp
;
1320 long lmqc
; /* Last Member Query Count */
1322 igmp
= group
->group_igmp_sock
;
1323 pim_ifp
= igmp
->interface
->info
;
1324 lmqc
= pim_ifp
->igmp_last_member_query_count
;
1326 /* lower group timer to lmqt */
1327 igmp_group_timer_lower_to_lmqt(group
);
1329 /* reset retransmission counter */
1330 group
->group_specific_query_retransmit_count
= lmqc
;
1332 /* immediately send group specific query (decrease retransmit counter by
1334 group_retransmit_group(group
);
1336 /* make sure group retransmit timer is running */
1337 group_retransmit_timer_on(group
);
1341 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1343 static void source_query_send_by_flag(struct igmp_group
*group
,
1344 int num_sources_tosend
)
1346 struct igmp_sock
*igmp
;
1347 struct pim_interface
*pim_ifp
;
1348 struct listnode
*src_node
;
1349 struct igmp_source
*src
;
1350 long lmqc
; /* Last Member Query Count */
1351 long lmqi_msec
; /* Last Member Query Interval */
1352 long lmqt_msec
; /* Last Member Query Time */
1354 zassert(num_sources_tosend
> 0);
1356 igmp
= group
->group_igmp_sock
;
1357 pim_ifp
= igmp
->interface
->info
;
1359 lmqc
= pim_ifp
->igmp_last_member_query_count
;
1360 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1361 lmqt_msec
= lmqc
* lmqi_msec
;
1364 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1367 (...) for each of the sources in X of group G, with source timer
1370 o Set number of retransmissions for each source to [Last Member
1372 o Lower source timer to LMQT.
1374 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1375 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1376 /* source "src" in X of group G */
1377 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1378 src
->source_query_retransmit_count
= lmqc
;
1379 igmp_source_timer_lower_to_lmqt(src
);
1384 /* send group-and-source specific queries */
1385 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1387 /* make sure group retransmit timer is running */
1388 group_retransmit_timer_on(group
);
1391 static void block_excl(struct igmp_group
*group
, int num_sources
,
1392 struct in_addr
*sources
)
1394 int num_sources_tosend
= 0;
1397 /* 1. clear off SEND flag from all known sources (X,Y) */
1398 source_clear_send_flag(group
->group_source_list
);
1400 /* 2. scan received sources (A) */
1401 for (i
= 0; i
< num_sources
; ++i
) {
1402 struct igmp_source
*source
;
1403 struct in_addr
*src_addr
;
1405 src_addr
= sources
+ i
;
1407 /* lookup reported source (A) in known sources (X,Y) */
1408 source
= igmp_find_source_by_addr(group
, *src_addr
);
1410 /* 3: if not found, create source with Group Timer:
1411 * (A-X-Y)=Group Timer */
1412 long group_timer_msec
;
1413 source
= source_new(group
, *src_addr
);
1415 zassert(!source
->t_source_timer
); /* timer == 0 */
1416 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1417 igmp_source_timer_on(group
, source
, group_timer_msec
);
1418 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1421 if (source
->t_source_timer
) {
1422 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1423 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1424 ++num_sources_tosend
;
1428 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1429 if (num_sources_tosend
> 0) {
1430 source_query_send_by_flag(group
, num_sources_tosend
);
1434 static void block_incl(struct igmp_group
*group
, int num_sources
,
1435 struct in_addr
*sources
)
1437 int num_sources_tosend
= 0;
1440 /* 1. clear off SEND flag from all known sources (B) */
1441 source_clear_send_flag(group
->group_source_list
);
1443 /* 2. scan received sources (A) */
1444 for (i
= 0; i
< num_sources
; ++i
) {
1445 struct igmp_source
*source
;
1446 struct in_addr
*src_addr
;
1448 src_addr
= sources
+ i
;
1450 /* lookup reported source (A) in known sources (B) */
1451 source
= igmp_find_source_by_addr(group
, *src_addr
);
1453 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1454 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1455 ++num_sources_tosend
;
1459 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1460 if (num_sources_tosend
> 0) {
1461 source_query_send_by_flag(group
, num_sources_tosend
);
1465 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1466 struct in_addr group_addr
, int num_sources
,
1467 struct in_addr
*sources
)
1469 struct interface
*ifp
= igmp
->interface
;
1470 struct igmp_group
*group
;
1472 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
1475 /* non-existant group is created as INCLUDE {empty} */
1476 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1481 if (group
->group_filtermode_isexcl
) {
1483 block_excl(group
, num_sources
, sources
);
1486 block_incl(group
, num_sources
, sources
);
1490 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1492 struct igmp_sock
*igmp
;
1493 struct interface
*ifp
;
1494 struct pim_interface
*pim_ifp
;
1496 int lmqi_dsec
; /* Last Member Query Interval */
1497 int lmqc
; /* Last Member Query Count */
1498 int lmqt_msec
; /* Last Member Query Time */
1501 RFC 3376: 6.2.2. Definition of Group Timers
1503 The group timer is only used when a group is in EXCLUDE mode and
1504 it represents the time for the *filter-mode* of the group to
1505 expire and switch to INCLUDE mode.
1507 if (!group
->group_filtermode_isexcl
) {
1511 igmp
= group
->group_igmp_sock
;
1512 ifp
= igmp
->interface
;
1513 pim_ifp
= ifp
->info
;
1516 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1517 lmqc
= pim_ifp
->igmp_last_member_query_count
;
1518 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1519 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1521 if (PIM_DEBUG_IGMP_TRACE
) {
1522 char group_str
[INET_ADDRSTRLEN
];
1523 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1526 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1527 __PRETTY_FUNCTION__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1531 zassert(group
->group_filtermode_isexcl
);
1533 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1536 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1538 struct igmp_group
*group
;
1539 struct igmp_sock
*igmp
;
1540 struct interface
*ifp
;
1541 struct pim_interface
*pim_ifp
;
1543 int lmqi_dsec
; /* Last Member Query Interval */
1544 int lmqc
; /* Last Member Query Count */
1545 int lmqt_msec
; /* Last Member Query Time */
1547 group
= source
->source_group
;
1548 igmp
= group
->group_igmp_sock
;
1549 ifp
= igmp
->interface
;
1550 pim_ifp
= ifp
->info
;
1553 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1554 lmqc
= pim_ifp
->igmp_last_member_query_count
;
1555 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1556 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1558 if (PIM_DEBUG_IGMP_TRACE
) {
1559 char group_str
[INET_ADDRSTRLEN
];
1560 char source_str
[INET_ADDRSTRLEN
];
1561 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1563 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1564 sizeof(source_str
));
1566 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1567 __PRETTY_FUNCTION__
, group_str
, source_str
, ifname
,
1568 lmqc
, lmqi_dsec
, lmqt_msec
);
1571 igmp_source_timer_on(group
, source
, lmqt_msec
);
1574 void igmp_v3_send_query(struct igmp_group
*group
, int fd
, const char *ifname
,
1575 char *query_buf
, int query_buf_size
, int num_sources
,
1576 struct in_addr dst_addr
, struct in_addr group_addr
,
1577 int query_max_response_time_dsec
, uint8_t s_flag
,
1578 uint8_t querier_robustness_variable
,
1579 uint16_t querier_query_interval
)
1582 uint8_t max_resp_code
;
1585 struct sockaddr_in to
;
1589 zassert(num_sources
>= 0);
1591 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1592 if (msg_size
> query_buf_size
) {
1595 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1596 __FILE__
, __PRETTY_FUNCTION__
, msg_size
,
1601 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1602 zassert((s_flag
== 0) || (s_flag
== 1));
1604 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1605 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1608 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1610 If non-zero, the QRV field contains the [Robustness Variable]
1611 value used by the querier, i.e., the sender of the Query. If the
1612 querier's [Robustness Variable] exceeds 7, the maximum value of
1613 the QRV field, the QRV is set to zero.
1615 if (querier_robustness_variable
> 7) {
1616 querier_robustness_variable
= 0;
1619 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1620 query_buf
[1] = max_resp_code
;
1621 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1622 0; /* for computing checksum */
1623 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1625 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1626 query_buf
[9] = qqic
;
1627 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1630 checksum
= in_cksum(query_buf
, msg_size
);
1631 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1633 if (PIM_DEBUG_IGMP_PACKETS
) {
1634 char dst_str
[INET_ADDRSTRLEN
];
1635 char group_str
[INET_ADDRSTRLEN
];
1636 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1637 pim_inet4_dump("<group?>", group_addr
, group_str
,
1640 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1641 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1642 s_flag
, querier_robustness_variable
,
1643 querier_query_interval
, qqic
);
1646 memset(&to
, 0, sizeof(to
));
1647 to
.sin_family
= AF_INET
;
1648 to
.sin_addr
= dst_addr
;
1651 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1652 (struct sockaddr
*)&to
, tolen
);
1653 if (sent
!= (ssize_t
)msg_size
) {
1654 char dst_str
[INET_ADDRSTRLEN
];
1655 char group_str
[INET_ADDRSTRLEN
];
1656 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1657 pim_inet4_dump("<group?>", group_addr
, group_str
,
1661 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1662 dst_str
, ifname
, group_str
, msg_size
, errno
,
1663 safe_strerror(errno
));
1666 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1667 dst_str
, ifname
, group_str
, msg_size
, sent
);
1673 s_flag sanity test: s_flag must be set for general queries
1675 RFC 3376: 6.6.1. Timer Updates
1677 When a router sends or receives a query with a clear Suppress
1678 Router-Side Processing flag, it must update its timers to reflect
1679 the correct timeout values for the group or sources being queried.
1681 General queries don't trigger timer update.
1684 /* general query? */
1685 if (PIM_INADDR_IS_ANY(group_addr
)) {
1686 char dst_str
[INET_ADDRSTRLEN
];
1687 char group_str
[INET_ADDRSTRLEN
];
1688 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1690 pim_inet4_dump("<group?>", group_addr
, group_str
,
1693 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1694 __PRETTY_FUNCTION__
, dst_str
, ifname
, group_str
,
1700 void igmp_v3_recv_query(struct igmp_sock
*igmp
, const char *from_str
,
1703 struct interface
*ifp
;
1704 struct pim_interface
*pim_ifp
;
1705 struct in_addr group_addr
;
1706 uint8_t resv_s_qrv
= 0;
1711 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1712 ifp
= igmp
->interface
;
1713 pim_ifp
= ifp
->info
;
1716 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1718 * Routers adopt the QRV value from the most recently received Query
1719 * as their own [Robustness Variable] value, unless that most
1720 * recently received QRV was zero, in which case the receivers use
1721 * the default [Robustness Variable] value specified in section 8.1
1722 * or a statically configured value.
1724 resv_s_qrv
= igmp_msg
[8];
1725 qrv
= 7 & resv_s_qrv
;
1726 igmp
->querier_robustness_variable
=
1727 qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1730 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1732 * Multicast routers that are not the current querier adopt the QQI
1733 * value from the most recently received Query as their own [Query
1734 * Interval] value, unless that most recently received QQI was zero,
1735 * in which case the receiving routers use the default.
1737 if (igmp
->t_other_querier_timer
) {
1738 /* other querier present */
1742 qqi
= igmp_msg_decode8to16(qqic
);
1743 igmp
->querier_query_interval
=
1744 qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1746 if (PIM_DEBUG_IGMP_TRACE
) {
1747 char ifaddr_str
[INET_ADDRSTRLEN
];
1748 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1749 sizeof(ifaddr_str
));
1751 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1753 qqi
? "recv-non-default" : "default",
1754 igmp
->querier_query_interval
, qqic
, from_str
);
1759 * RFC 3376: 6.6.1. Timer Updates
1761 * When a router sends or receives a query with a clear Suppress
1762 * Router-Side Processing flag, it must update its timers to reflect
1763 * the correct timeout values for the group or sources being queried.
1765 * General queries don't trigger timer update.
1767 s_flag
= (1 << 3) & resv_s_qrv
;
1770 /* s_flag is clear */
1772 if (PIM_INADDR_IS_ANY(group_addr
)) {
1773 /* this is a general query */
1774 /* log that general query should have the s_flag set */
1776 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1777 from_str
, ifp
->name
);
1779 struct igmp_group
*group
;
1781 /* this is a non-general query: perform timer updates */
1783 group
= find_group_by_addr(igmp
, group_addr
);
1785 int recv_num_sources
= ntohs(*(
1788 + IGMP_V3_NUMSOURCES_OFFSET
));
1791 * RFC 3376: 6.6.1. Timer Updates
1792 * Query Q(G,A): Source Timer for sources in A
1793 * are lowered to LMQT
1794 * Query Q(G): Group Timer is lowered to LMQT
1796 if (recv_num_sources
< 1) {
1797 /* Query Q(G): Group Timer is lowered to
1800 igmp_group_timer_lower_to_lmqt(group
);
1802 /* Query Q(G,A): Source Timer for
1803 * sources in A are lowered to LMQT */
1805 /* Scan sources in query and lower their
1807 struct in_addr
*sources
=
1810 + IGMP_V3_SOURCES_OFFSET
);
1811 for (i
= 0; i
< recv_num_sources
; ++i
) {
1812 struct in_addr src_addr
;
1813 struct igmp_source
*src
;
1814 memcpy(&src_addr
, sources
+ i
,
1815 sizeof(struct in_addr
));
1816 src
= igmp_find_source_by_addr(
1819 igmp_source_timer_lower_to_lmqt(
1825 char group_str
[INET_ADDRSTRLEN
];
1826 pim_inet4_dump("<group?>", group_addr
,
1827 group_str
, sizeof(group_str
));
1829 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1830 from_str
, ifp
->name
, group_str
);
1833 } /* s_flag is clear: timer updates */
1836 int igmp_v3_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
1837 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1839 uint16_t recv_checksum
;
1842 uint8_t *group_record
;
1843 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1844 struct interface
*ifp
= igmp
->interface
;
1847 struct pim_interface
*pim_ifp
;
1849 if (igmp
->mtrace_only
)
1852 pim_ifp
= igmp
->interface
->info
;
1854 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1856 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1857 from_str
, ifp
->name
, igmp_msg_len
,
1858 IGMP_V3_MSG_MIN_SIZE
);
1862 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1864 /* for computing checksum */
1865 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1867 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1868 if (checksum
!= recv_checksum
) {
1870 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1871 from_str
, ifp
->name
, recv_checksum
, checksum
);
1875 /* Collecting IGMP Rx stats */
1876 igmp
->rx_stats
.report_v3
++;
1879 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1880 if (num_groups
< 1) {
1882 "Recv IGMP report v3 from %s on %s: missing group records",
1883 from_str
, ifp
->name
);
1887 if (PIM_DEBUG_IGMP_PACKETS
) {
1889 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1890 from_str
, ifp
->name
, igmp_msg_len
, checksum
,
1894 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1897 for (i
= 0; i
< num_groups
; ++i
) {
1898 struct in_addr rec_group
;
1903 int rec_num_sources
;
1907 bool filtered
= false;
1909 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1912 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1913 from_str
, ifp
->name
);
1917 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1919 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1920 rec_num_sources
= ntohs(*(
1921 uint16_t *)(group_record
1922 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1925 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1926 sizeof(struct in_addr
));
1928 if (PIM_DEBUG_IGMP_PACKETS
) {
1930 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1931 from_str
, ifp
->name
, i
, rec_type
,
1932 rec_auxdatalen
, rec_num_sources
,
1933 inet_ntoa(rec_group
));
1938 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1940 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1942 if ((src
+ 4) > report_pastend
) {
1944 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1945 from_str
, ifp
->name
);
1949 if (PIM_DEBUG_IGMP_PACKETS
) {
1952 if (!inet_ntop(AF_INET
, src
, src_str
,
1954 sprintf(src_str
, "<source?>");
1957 " Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1958 from_str
, ifp
->name
, i
,
1959 inet_ntoa(rec_group
), src_str
);
1961 } /* for (sources) */
1964 lncb
.family
= AF_INET
;
1965 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1966 lncb
.prefixlen
= 24;
1969 g
.u
.prefix4
= rec_group
;
1972 /* determine filtering status for group */
1973 filtered
= pim_is_group_filtered(ifp
->info
, &rec_group
);
1975 if (PIM_DEBUG_IGMP_PACKETS
&& filtered
)
1977 "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
1978 inet_ntoa(rec_group
), from_str
, ifp
->name
,
1979 pim_ifp
->boundary_oil_plist
);
1982 * If we receive a igmp report with the group in 224.0.0.0/24
1983 * then we should ignore it
1985 if (prefix_match(&lncb
, &g
))
1988 if (!local_ncb
&& !filtered
)
1990 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1991 igmpv3_report_isin(igmp
, from
, rec_group
,
1993 (struct in_addr
*)sources
);
1995 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1997 igmp
, from
, rec_group
, rec_num_sources
,
1998 (struct in_addr
*)sources
, 0);
2000 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
2001 igmpv3_report_toin(igmp
, from
, rec_group
,
2003 (struct in_addr
*)sources
);
2005 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
2006 igmpv3_report_toex(igmp
, from
, rec_group
,
2008 (struct in_addr
*)sources
);
2010 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2011 igmpv3_report_allow(igmp
, from
, rec_group
,
2013 (struct in_addr
*)sources
);
2015 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2016 igmpv3_report_block(igmp
, from
, rec_group
,
2018 (struct in_addr
*)sources
);
2022 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2023 from_str
, ifp
->name
, rec_type
);
2027 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2030 } /* for (group records) */