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 /* non-existant group is created as INCLUDE {empty} */
675 group
= igmp_add_group_by_addr(igmp
, group_addr
);
680 /* So we can display how we learned the group in our show command output
682 if (from_igmp_v2_report
)
683 group
->igmp_version
= 2;
685 if (group
->group_filtermode_isexcl
) {
687 isex_excl(group
, num_sources
, sources
);
690 isex_incl(group
, num_sources
, sources
);
691 zassert(group
->group_filtermode_isexcl
);
694 zassert(group
->group_filtermode_isexcl
);
696 igmp_group_reset_gmi(group
);
699 static void toin_incl(struct igmp_group
*group
, int num_sources
,
700 struct in_addr
*sources
)
702 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
703 int num_sources_tosend
= listcount(group
->group_source_list
);
706 /* Set SEND flag for all known sources (A) */
707 source_mark_send_flag(group
);
709 /* Scan received sources (B) */
710 for (i
= 0; i
< num_sources
; ++i
) {
711 struct igmp_source
*source
;
712 struct in_addr
*src_addr
;
714 src_addr
= sources
+ i
;
716 /* Lookup reported source (B) */
717 source
= igmp_find_source_by_addr(group
, *src_addr
);
719 /* If found, clear SEND flag (A*B) */
720 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
721 --num_sources_tosend
;
723 /* If not found, create new source */
724 source
= source_new(group
, *src_addr
);
726 /* ugh, internal malloc failure, skip source */
732 igmp_source_reset_gmi(igmp
, group
, source
);
735 /* Send sources marked with SEND flag: Q(G,A-B) */
736 if (num_sources_tosend
> 0) {
737 source_query_send_by_flag(group
, num_sources_tosend
);
741 static void toin_excl(struct igmp_group
*group
, int num_sources
,
742 struct in_addr
*sources
)
744 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
745 int num_sources_tosend
;
748 /* Set SEND flag for X (sources with timer > 0) */
749 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
751 /* Scan received sources (A) */
752 for (i
= 0; i
< num_sources
; ++i
) {
753 struct igmp_source
*source
;
754 struct in_addr
*src_addr
;
756 src_addr
= sources
+ i
;
758 /* Lookup reported source (A) */
759 source
= igmp_find_source_by_addr(group
, *src_addr
);
761 if (source
->t_source_timer
) {
762 /* If found and timer running, clear SEND flag
764 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
765 --num_sources_tosend
;
768 /* If not found, create new source */
769 source
= source_new(group
, *src_addr
);
771 /* ugh, internal malloc failure, skip source */
777 igmp_source_reset_gmi(igmp
, group
, source
);
780 /* Send sources marked with SEND flag: Q(G,X-A) */
781 if (num_sources_tosend
> 0) {
782 source_query_send_by_flag(group
, num_sources_tosend
);
786 group_query_send(group
);
789 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
790 struct in_addr group_addr
, int num_sources
,
791 struct in_addr
*sources
)
793 struct interface
*ifp
= igmp
->interface
;
794 struct igmp_group
*group
;
796 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
800 * If the requested filter mode is INCLUDE *and* the requested source
801 * list is empty, then the entry corresponding to the requested
802 * interface and multicast address is deleted if present. If no such
803 * entry is present, the request is ignored.
806 /* non-existant group is created as INCLUDE {empty} */
807 group
= igmp_add_group_by_addr(igmp
, group_addr
);
812 group
= find_group_by_addr(igmp
, group_addr
);
817 if (group
->group_filtermode_isexcl
) {
819 toin_excl(group
, num_sources
, sources
);
822 toin_incl(group
, num_sources
, sources
);
826 static void toex_incl(struct igmp_group
*group
, int num_sources
,
827 struct in_addr
*sources
)
829 int num_sources_tosend
= 0;
832 zassert(!group
->group_filtermode_isexcl
);
834 /* Set DELETE flag for all known sources (A) */
835 source_mark_delete_flag(group
);
837 /* Clear off SEND flag from all known sources (A) */
838 source_clear_send_flag(group
->group_source_list
);
840 /* Scan received sources (B) */
841 for (i
= 0; i
< num_sources
; ++i
) {
842 struct igmp_source
*source
;
843 struct in_addr
*src_addr
;
845 src_addr
= sources
+ i
;
847 /* Lookup reported source (B) */
848 source
= igmp_find_source_by_addr(group
, *src_addr
);
850 /* If found, clear deletion flag: (A*B) */
851 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
852 /* and set SEND flag (A*B) */
853 IGMP_SOURCE_DO_SEND(source
->source_flags
);
854 ++num_sources_tosend
;
856 /* If source not found, create source with timer=0:
858 source
= source_new(group
, *src_addr
);
860 /* ugh, internal malloc failure, skip source */
863 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
866 } /* Scan received sources (B) */
868 group
->group_filtermode_isexcl
= 1; /* boolean=true */
870 /* Delete all sources marked with DELETE flag (A-B) */
871 source_delete_by_flag(group
->group_source_list
);
873 /* Send sources marked with SEND flag: Q(G,A*B) */
874 if (num_sources_tosend
> 0) {
875 source_query_send_by_flag(group
, num_sources_tosend
);
878 zassert(group
->group_filtermode_isexcl
);
880 group_exclude_fwd_anysrc_ifempty(group
);
883 static void toex_excl(struct igmp_group
*group
, int num_sources
,
884 struct in_addr
*sources
)
886 int num_sources_tosend
= 0;
889 /* set DELETE flag for all known sources (X,Y) */
890 source_mark_delete_flag(group
);
892 /* clear off SEND flag from all known sources (X,Y) */
893 source_clear_send_flag(group
->group_source_list
);
895 if (num_sources
== 0) {
896 struct igmp_source
*source
;
897 struct in_addr any
= {.s_addr
= INADDR_ANY
};
899 source
= igmp_find_source_by_addr(group
, any
);
901 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
904 /* scan received sources (A) */
905 for (i
= 0; i
< num_sources
; ++i
) {
906 struct igmp_source
*source
;
907 struct in_addr
*src_addr
;
909 src_addr
= sources
+ i
;
911 /* lookup reported source (A) in known sources (X,Y) */
912 source
= igmp_find_source_by_addr(group
, *src_addr
);
914 /* if found, clear off DELETE flag from reported source
916 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
918 /* if not found, create source with Group Timer:
919 * (A-X-Y)=Group Timer */
920 long group_timer_msec
;
921 source
= source_new(group
, *src_addr
);
923 /* ugh, internal malloc failure, skip source */
927 zassert(!source
->t_source_timer
); /* timer == 0 */
928 group_timer_msec
= igmp_group_timer_remain_msec(group
);
929 igmp_source_timer_on(group
, source
, group_timer_msec
);
930 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
932 /* make sure source is created with DELETE flag unset */
933 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
936 /* make sure reported source has DELETE flag unset */
937 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
939 if (source
->t_source_timer
) {
940 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
941 IGMP_SOURCE_DO_SEND(source
->source_flags
);
942 ++num_sources_tosend
;
945 } /* scan received sources (A) */
948 delete all sources marked with DELETE flag:
952 source_delete_by_flag(group
->group_source_list
);
954 /* send sources marked with SEND flag: Q(G,A-Y) */
955 if (num_sources_tosend
> 0) {
956 source_query_send_by_flag(group
, num_sources_tosend
);
960 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
961 struct in_addr group_addr
, int num_sources
,
962 struct in_addr
*sources
)
964 struct interface
*ifp
= igmp
->interface
;
965 struct igmp_group
*group
;
967 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
970 /* non-existant group is created as INCLUDE {empty} */
971 group
= igmp_add_group_by_addr(igmp
, group_addr
);
976 if (group
->group_filtermode_isexcl
) {
978 toex_excl(group
, num_sources
, sources
);
981 toex_incl(group
, num_sources
, sources
);
982 zassert(group
->group_filtermode_isexcl
);
984 zassert(group
->group_filtermode_isexcl
);
986 /* Group Timer=GMI */
987 igmp_group_reset_gmi(group
);
990 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
991 struct in_addr group_addr
, int num_sources
,
992 struct in_addr
*sources
)
994 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
995 num_sources
, sources
);
997 allow(igmp
, from
, group_addr
, num_sources
, sources
);
1001 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1003 When transmitting a group specific query, if the group timer is
1004 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1005 in the query message.
1007 static void group_retransmit_group(struct igmp_group
*group
)
1009 struct igmp_sock
*igmp
;
1010 struct pim_interface
*pim_ifp
;
1011 long lmqc
; /* Last Member Query Count */
1012 long lmqi_msec
; /* Last Member Query Interval */
1013 long lmqt_msec
; /* Last Member Query Time */
1017 igmp
= group
->group_igmp_sock
;
1018 pim_ifp
= igmp
->interface
->info
;
1020 if (pim_ifp
->igmp_version
== 3) {
1021 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1023 query_buf_size
= IGMP_V12_MSG_SIZE
;
1026 char query_buf
[query_buf_size
];
1028 lmqc
= igmp
->querier_robustness_variable
;
1029 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1030 lmqt_msec
= lmqc
* lmqi_msec
;
1033 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1035 When transmitting a group specific query, if the group timer is
1036 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1037 in the query message.
1039 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1041 if (PIM_DEBUG_IGMP_TRACE
) {
1042 char group_str
[INET_ADDRSTRLEN
];
1043 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1046 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1047 group_str
, igmp
->interface
->name
, s_flag
,
1048 group
->group_specific_query_retransmit_count
);
1052 RFC3376: 4.1.12. IP Destination Addresses for Queries
1054 Group-Specific and Group-and-Source-Specific Queries are sent with
1055 an IP destination address equal to the multicast address of
1059 igmp_send_query(pim_ifp
->igmp_version
, group
, igmp
->fd
,
1060 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
1061 0 /* num_sources_tosend */,
1062 group
->group_addr
/* dst_addr */,
1063 group
->group_addr
/* group_addr */,
1064 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1065 s_flag
, igmp
->querier_robustness_variable
,
1066 igmp
->querier_query_interval
);
1070 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1072 When building a group and source specific query for a group G, two
1073 separate query messages are sent for the group. The first one has
1074 the "Suppress Router-Side Processing" bit set and contains all the
1075 sources with retransmission state and timers greater than LMQT. The
1076 second has the "Suppress Router-Side Processing" bit clear and
1077 contains all the sources with retransmission state and timers lower
1078 or equal to LMQT. If either of the two calculated messages does not
1079 contain any sources, then its transmission is suppressed.
1081 static int group_retransmit_sources(struct igmp_group
*group
,
1082 int send_with_sflag_set
)
1084 struct igmp_sock
*igmp
;
1085 struct pim_interface
*pim_ifp
;
1086 long lmqc
; /* Last Member Query Count */
1087 long lmqi_msec
; /* Last Member Query Interval */
1088 long lmqt_msec
; /* Last Member Query Time */
1089 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1090 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1091 int query_buf1_max_sources
;
1092 int query_buf2_max_sources
;
1093 struct in_addr
*source_addr1
;
1094 struct in_addr
*source_addr2
;
1095 int num_sources_tosend1
;
1096 int num_sources_tosend2
;
1097 struct listnode
*src_node
;
1098 struct igmp_source
*src
;
1099 int num_retransmit_sources_left
= 0;
1101 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1102 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1104 igmp
= group
->group_igmp_sock
;
1105 pim_ifp
= igmp
->interface
->info
;
1107 lmqc
= igmp
->querier_robustness_variable
;
1108 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1109 lmqt_msec
= lmqc
* lmqi_msec
;
1111 /* Scan all group sources */
1112 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1114 /* Source has retransmission state? */
1115 if (src
->source_query_retransmit_count
< 1)
1118 if (--src
->source_query_retransmit_count
> 0) {
1119 ++num_retransmit_sources_left
;
1122 /* Copy source address into appropriate query buffer */
1123 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1124 *source_addr1
= src
->source_addr
;
1127 *source_addr2
= src
->source_addr
;
1132 num_sources_tosend1
=
1134 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1135 num_sources_tosend2
=
1137 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1139 if (PIM_DEBUG_IGMP_TRACE
) {
1140 char group_str
[INET_ADDRSTRLEN
];
1141 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1144 "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",
1145 group_str
, igmp
->interface
->name
, num_sources_tosend1
,
1146 num_sources_tosend2
, send_with_sflag_set
,
1147 num_retransmit_sources_left
);
1150 if (num_sources_tosend1
> 0) {
1152 Send group-and-source-specific query with s_flag set and all
1153 sources with timers greater than LMQT.
1156 if (send_with_sflag_set
) {
1158 query_buf1_max_sources
=
1159 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1161 if (num_sources_tosend1
> query_buf1_max_sources
) {
1162 char group_str
[INET_ADDRSTRLEN
];
1163 pim_inet4_dump("<group?>", group
->group_addr
,
1164 group_str
, sizeof(group_str
));
1166 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1167 __PRETTY_FUNCTION__
, group_str
,
1168 igmp
->interface
->name
,
1169 num_sources_tosend1
, sizeof(query_buf1
),
1170 query_buf1_max_sources
);
1173 RFC3376: 4.1.12. IP Destination Addresses for
1176 Group-Specific and Group-and-Source-Specific
1177 Queries are sent with
1178 an IP destination address equal to the
1179 multicast address of
1184 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1185 igmp
->interface
->name
, query_buf1
,
1186 sizeof(query_buf1
), num_sources_tosend1
,
1187 group
->group_addr
, group
->group_addr
,
1188 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1190 igmp
->querier_robustness_variable
,
1191 igmp
->querier_query_interval
);
1194 } /* send_with_sflag_set */
1197 if (num_sources_tosend2
> 0) {
1199 Send group-and-source-specific query with s_flag clear and all
1200 sources with timers lower or equal to LMQT.
1203 query_buf2_max_sources
=
1204 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1205 if (num_sources_tosend2
> query_buf2_max_sources
) {
1206 char group_str
[INET_ADDRSTRLEN
];
1207 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1210 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1211 __PRETTY_FUNCTION__
, group_str
,
1212 igmp
->interface
->name
, num_sources_tosend2
,
1213 sizeof(query_buf2
), query_buf2_max_sources
);
1216 RFC3376: 4.1.12. IP Destination Addresses for Queries
1218 Group-Specific and Group-and-Source-Specific Queries
1220 an IP destination address equal to the multicast
1226 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1227 igmp
->interface
->name
, query_buf2
,
1228 sizeof(query_buf2
), num_sources_tosend2
,
1229 group
->group_addr
, group
->group_addr
,
1230 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1232 igmp
->querier_robustness_variable
,
1233 igmp
->querier_query_interval
);
1237 return num_retransmit_sources_left
;
1240 static int igmp_group_retransmit(struct thread
*t
)
1242 struct igmp_group
*group
;
1243 int num_retransmit_sources_left
;
1244 int send_with_sflag_set
; /* boolean */
1246 group
= THREAD_ARG(t
);
1248 if (PIM_DEBUG_IGMP_TRACE
) {
1249 char group_str
[INET_ADDRSTRLEN
];
1250 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1252 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1253 group
->group_igmp_sock
->interface
->name
);
1256 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1257 if (group
->group_specific_query_retransmit_count
> 0) {
1259 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1260 group_retransmit_group(group
);
1261 --group
->group_specific_query_retransmit_count
;
1265 If a group specific query is scheduled to be transmitted at
1267 same time as a group and source specific query for the same
1269 then transmission of the group and source specific message
1271 "Suppress Router-Side Processing" bit set may be suppressed.
1273 send_with_sflag_set
= 0; /* boolean=false */
1275 send_with_sflag_set
= 1; /* boolean=true */
1278 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1279 num_retransmit_sources_left
=
1280 group_retransmit_sources(group
, send_with_sflag_set
);
1283 Keep group retransmit timer running if there is any retransmit
1286 if ((num_retransmit_sources_left
> 0)
1287 || (group
->group_specific_query_retransmit_count
> 0)) {
1288 group_retransmit_timer_on(group
);
1295 group_retransmit_timer_on:
1296 if group retransmit timer isn't running, starts it;
1297 otherwise, do nothing
1299 static void group_retransmit_timer_on(struct igmp_group
*group
)
1301 struct igmp_sock
*igmp
;
1302 struct pim_interface
*pim_ifp
;
1303 long lmqi_msec
; /* Last Member Query Interval */
1305 /* if group retransmit timer is running, do nothing */
1306 if (group
->t_group_query_retransmit_timer
) {
1310 igmp
= group
->group_igmp_sock
;
1311 pim_ifp
= igmp
->interface
->info
;
1313 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1315 if (PIM_DEBUG_IGMP_TRACE
) {
1316 char group_str
[INET_ADDRSTRLEN
];
1317 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1320 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1321 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1322 igmp
->interface
->name
);
1325 thread_add_timer_msec(master
, igmp_group_retransmit
, group
, lmqi_msec
,
1326 &group
->t_group_query_retransmit_timer
);
1329 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1331 return pim_time_timer_remain_msec(group
->t_group_timer
);
1334 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1336 return pim_time_timer_remain_msec(source
->t_source_timer
);
1340 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1342 static void group_query_send(struct igmp_group
*group
)
1344 long lmqc
; /* Last Member Query Count */
1346 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1348 /* lower group timer to lmqt */
1349 igmp_group_timer_lower_to_lmqt(group
);
1351 /* reset retransmission counter */
1352 group
->group_specific_query_retransmit_count
= lmqc
;
1354 /* immediately send group specific query (decrease retransmit counter by
1356 group_retransmit_group(group
);
1358 /* make sure group retransmit timer is running */
1359 group_retransmit_timer_on(group
);
1363 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1365 static void source_query_send_by_flag(struct igmp_group
*group
,
1366 int num_sources_tosend
)
1368 struct igmp_sock
*igmp
;
1369 struct pim_interface
*pim_ifp
;
1370 struct listnode
*src_node
;
1371 struct igmp_source
*src
;
1372 long lmqc
; /* Last Member Query Count */
1373 long lmqi_msec
; /* Last Member Query Interval */
1374 long lmqt_msec
; /* Last Member Query Time */
1376 zassert(num_sources_tosend
> 0);
1378 igmp
= group
->group_igmp_sock
;
1379 pim_ifp
= igmp
->interface
->info
;
1381 lmqc
= igmp
->querier_robustness_variable
;
1382 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1383 lmqt_msec
= lmqc
* lmqi_msec
;
1386 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1389 (...) for each of the sources in X of group G, with source timer
1392 o Set number of retransmissions for each source to [Last Member
1394 o Lower source timer to LMQT.
1396 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1397 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1398 /* source "src" in X of group G */
1399 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1400 src
->source_query_retransmit_count
= lmqc
;
1401 igmp_source_timer_lower_to_lmqt(src
);
1406 /* send group-and-source specific queries */
1407 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1409 /* make sure group retransmit timer is running */
1410 group_retransmit_timer_on(group
);
1413 static void block_excl(struct igmp_group
*group
, int num_sources
,
1414 struct in_addr
*sources
)
1416 int num_sources_tosend
= 0;
1419 /* 1. clear off SEND flag from all known sources (X,Y) */
1420 source_clear_send_flag(group
->group_source_list
);
1422 /* 2. scan received sources (A) */
1423 for (i
= 0; i
< num_sources
; ++i
) {
1424 struct igmp_source
*source
;
1425 struct in_addr
*src_addr
;
1427 src_addr
= sources
+ i
;
1429 /* lookup reported source (A) in known sources (X,Y) */
1430 source
= igmp_find_source_by_addr(group
, *src_addr
);
1432 /* 3: if not found, create source with Group Timer:
1433 * (A-X-Y)=Group Timer */
1434 long group_timer_msec
;
1435 source
= source_new(group
, *src_addr
);
1437 /* ugh, internal malloc failure, skip source */
1441 zassert(!source
->t_source_timer
); /* timer == 0 */
1442 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1443 igmp_source_timer_on(group
, source
, group_timer_msec
);
1444 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1447 if (source
->t_source_timer
) {
1448 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1449 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1450 ++num_sources_tosend
;
1454 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1455 if (num_sources_tosend
> 0) {
1456 source_query_send_by_flag(group
, num_sources_tosend
);
1460 static void block_incl(struct igmp_group
*group
, int num_sources
,
1461 struct in_addr
*sources
)
1463 int num_sources_tosend
= 0;
1466 /* 1. clear off SEND flag from all known sources (B) */
1467 source_clear_send_flag(group
->group_source_list
);
1469 /* 2. scan received sources (A) */
1470 for (i
= 0; i
< num_sources
; ++i
) {
1471 struct igmp_source
*source
;
1472 struct in_addr
*src_addr
;
1474 src_addr
= sources
+ i
;
1476 /* lookup reported source (A) in known sources (B) */
1477 source
= igmp_find_source_by_addr(group
, *src_addr
);
1479 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1480 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1481 ++num_sources_tosend
;
1485 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1486 if (num_sources_tosend
> 0) {
1487 source_query_send_by_flag(group
, num_sources_tosend
);
1491 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1492 struct in_addr group_addr
, int num_sources
,
1493 struct in_addr
*sources
)
1495 struct interface
*ifp
= igmp
->interface
;
1496 struct igmp_group
*group
;
1498 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
1501 /* non-existant group is created as INCLUDE {empty} */
1502 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1507 if (group
->group_filtermode_isexcl
) {
1509 block_excl(group
, num_sources
, sources
);
1512 block_incl(group
, num_sources
, sources
);
1516 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1518 struct igmp_sock
*igmp
;
1519 struct interface
*ifp
;
1520 struct pim_interface
*pim_ifp
;
1522 int lmqi_dsec
; /* Last Member Query Interval */
1523 int lmqc
; /* Last Member Query Count */
1524 int lmqt_msec
; /* Last Member Query Time */
1527 RFC 3376: 6.2.2. Definition of Group Timers
1529 The group timer is only used when a group is in EXCLUDE mode and
1530 it represents the time for the *filter-mode* of the group to
1531 expire and switch to INCLUDE mode.
1533 if (!group
->group_filtermode_isexcl
) {
1537 igmp
= group
->group_igmp_sock
;
1538 ifp
= igmp
->interface
;
1539 pim_ifp
= ifp
->info
;
1542 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1543 lmqc
= igmp
->querier_robustness_variable
;
1544 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1545 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1547 if (PIM_DEBUG_IGMP_TRACE
) {
1548 char group_str
[INET_ADDRSTRLEN
];
1549 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1552 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1553 __PRETTY_FUNCTION__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1557 zassert(group
->group_filtermode_isexcl
);
1559 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1562 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1564 struct igmp_group
*group
;
1565 struct igmp_sock
*igmp
;
1566 struct interface
*ifp
;
1567 struct pim_interface
*pim_ifp
;
1569 int lmqi_dsec
; /* Last Member Query Interval */
1570 int lmqc
; /* Last Member Query Count */
1571 int lmqt_msec
; /* Last Member Query Time */
1573 group
= source
->source_group
;
1574 igmp
= group
->group_igmp_sock
;
1575 ifp
= igmp
->interface
;
1576 pim_ifp
= ifp
->info
;
1579 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1580 lmqc
= igmp
->querier_robustness_variable
;
1581 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1582 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1584 if (PIM_DEBUG_IGMP_TRACE
) {
1585 char group_str
[INET_ADDRSTRLEN
];
1586 char source_str
[INET_ADDRSTRLEN
];
1587 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1589 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1590 sizeof(source_str
));
1592 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1593 __PRETTY_FUNCTION__
, group_str
, source_str
, ifname
,
1594 lmqc
, lmqi_dsec
, lmqt_msec
);
1597 igmp_source_timer_on(group
, source
, lmqt_msec
);
1600 void igmp_v3_send_query(struct igmp_group
*group
, int fd
, const char *ifname
,
1601 char *query_buf
, int query_buf_size
, int num_sources
,
1602 struct in_addr dst_addr
, struct in_addr group_addr
,
1603 int query_max_response_time_dsec
, uint8_t s_flag
,
1604 uint8_t querier_robustness_variable
,
1605 uint16_t querier_query_interval
)
1608 uint8_t max_resp_code
;
1611 struct sockaddr_in to
;
1615 zassert(num_sources
>= 0);
1617 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1618 if (msg_size
> query_buf_size
) {
1620 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1621 __FILE__
, __PRETTY_FUNCTION__
, msg_size
,
1626 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1627 zassert((s_flag
== 0) || (s_flag
== 1));
1629 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1630 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1633 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1635 If non-zero, the QRV field contains the [Robustness Variable]
1636 value used by the querier, i.e., the sender of the Query. If the
1637 querier's [Robustness Variable] exceeds 7, the maximum value of
1638 the QRV field, the QRV is set to zero.
1640 if (querier_robustness_variable
> 7) {
1641 querier_robustness_variable
= 0;
1644 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1645 query_buf
[1] = max_resp_code
;
1646 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1647 0; /* for computing checksum */
1648 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1650 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1651 query_buf
[9] = qqic
;
1652 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1655 checksum
= in_cksum(query_buf
, msg_size
);
1656 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1658 if (PIM_DEBUG_IGMP_PACKETS
) {
1659 char dst_str
[INET_ADDRSTRLEN
];
1660 char group_str
[INET_ADDRSTRLEN
];
1661 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1662 pim_inet4_dump("<group?>", group_addr
, group_str
,
1665 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1666 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1667 s_flag
, querier_robustness_variable
,
1668 querier_query_interval
, qqic
);
1671 memset(&to
, 0, sizeof(to
));
1672 to
.sin_family
= AF_INET
;
1673 to
.sin_addr
= dst_addr
;
1676 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1677 (struct sockaddr
*)&to
, tolen
);
1678 if (sent
!= (ssize_t
)msg_size
) {
1679 char dst_str
[INET_ADDRSTRLEN
];
1680 char group_str
[INET_ADDRSTRLEN
];
1681 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1682 pim_inet4_dump("<group?>", group_addr
, group_str
,
1686 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1687 dst_str
, ifname
, group_str
, msg_size
, errno
,
1688 safe_strerror(errno
));
1691 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1692 dst_str
, ifname
, group_str
, msg_size
, sent
);
1698 s_flag sanity test: s_flag must be set for general queries
1700 RFC 3376: 6.6.1. Timer Updates
1702 When a router sends or receives a query with a clear Suppress
1703 Router-Side Processing flag, it must update its timers to reflect
1704 the correct timeout values for the group or sources being queried.
1706 General queries don't trigger timer update.
1709 /* general query? */
1710 if (PIM_INADDR_IS_ANY(group_addr
)) {
1711 char dst_str
[INET_ADDRSTRLEN
];
1712 char group_str
[INET_ADDRSTRLEN
];
1713 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1715 pim_inet4_dump("<group?>", group_addr
, group_str
,
1718 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1719 __PRETTY_FUNCTION__
, dst_str
, ifname
, group_str
,
1725 void igmp_v3_recv_query(struct igmp_sock
*igmp
, const char *from_str
,
1728 struct interface
*ifp
;
1729 struct pim_interface
*pim_ifp
;
1730 struct in_addr group_addr
;
1731 uint8_t resv_s_qrv
= 0;
1736 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1737 ifp
= igmp
->interface
;
1738 pim_ifp
= ifp
->info
;
1741 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1743 * Routers adopt the QRV value from the most recently received Query
1744 * as their own [Robustness Variable] value, unless that most
1745 * recently received QRV was zero, in which case the receivers use
1746 * the default [Robustness Variable] value specified in section 8.1
1747 * or a statically configured value.
1749 resv_s_qrv
= igmp_msg
[8];
1750 qrv
= 7 & resv_s_qrv
;
1751 igmp
->querier_robustness_variable
=
1752 qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1755 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1757 * Multicast routers that are not the current querier adopt the QQI
1758 * value from the most recently received Query as their own [Query
1759 * Interval] value, unless that most recently received QQI was zero,
1760 * in which case the receiving routers use the default.
1762 if (igmp
->t_other_querier_timer
) {
1763 /* other querier present */
1767 qqi
= igmp_msg_decode8to16(qqic
);
1768 igmp
->querier_query_interval
=
1769 qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1771 if (PIM_DEBUG_IGMP_TRACE
) {
1772 char ifaddr_str
[INET_ADDRSTRLEN
];
1773 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1774 sizeof(ifaddr_str
));
1776 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1778 qqi
? "recv-non-default" : "default",
1779 igmp
->querier_query_interval
, qqic
, from_str
);
1784 * RFC 3376: 6.6.1. Timer Updates
1786 * When a router sends or receives a query with a clear Suppress
1787 * Router-Side Processing flag, it must update its timers to reflect
1788 * the correct timeout values for the group or sources being queried.
1790 * General queries don't trigger timer update.
1792 s_flag
= (1 << 3) & resv_s_qrv
;
1795 /* s_flag is clear */
1797 if (PIM_INADDR_IS_ANY(group_addr
)) {
1798 /* this is a general query */
1799 /* log that general query should have the s_flag set */
1801 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1802 from_str
, ifp
->name
);
1804 struct igmp_group
*group
;
1806 /* this is a non-general query: perform timer updates */
1808 group
= find_group_by_addr(igmp
, group_addr
);
1810 int recv_num_sources
= ntohs(*(
1813 + IGMP_V3_NUMSOURCES_OFFSET
));
1816 * RFC 3376: 6.6.1. Timer Updates
1817 * Query Q(G,A): Source Timer for sources in A
1818 * are lowered to LMQT
1819 * Query Q(G): Group Timer is lowered to LMQT
1821 if (recv_num_sources
< 1) {
1822 /* Query Q(G): Group Timer is lowered to
1825 igmp_group_timer_lower_to_lmqt(group
);
1827 /* Query Q(G,A): Source Timer for
1828 * sources in A are lowered to LMQT */
1830 /* Scan sources in query and lower their
1832 struct in_addr
*sources
=
1835 + IGMP_V3_SOURCES_OFFSET
);
1836 for (i
= 0; i
< recv_num_sources
; ++i
) {
1837 struct in_addr src_addr
;
1838 struct igmp_source
*src
;
1839 memcpy(&src_addr
, sources
+ i
,
1840 sizeof(struct in_addr
));
1841 src
= igmp_find_source_by_addr(
1844 igmp_source_timer_lower_to_lmqt(
1850 char group_str
[INET_ADDRSTRLEN
];
1851 pim_inet4_dump("<group?>", group_addr
,
1852 group_str
, sizeof(group_str
));
1854 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1855 from_str
, ifp
->name
, group_str
);
1858 } /* s_flag is clear: timer updates */
1861 int igmp_v3_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
1862 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1864 uint16_t recv_checksum
;
1867 uint8_t *group_record
;
1868 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1869 struct interface
*ifp
= igmp
->interface
;
1873 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1875 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1876 from_str
, ifp
->name
, igmp_msg_len
,
1877 IGMP_V3_MSG_MIN_SIZE
);
1881 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1883 /* for computing checksum */
1884 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1886 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1887 if (checksum
!= recv_checksum
) {
1889 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1890 from_str
, ifp
->name
, recv_checksum
, checksum
);
1895 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1896 if (num_groups
< 1) {
1898 "Recv IGMP report v3 from %s on %s: missing group records",
1899 from_str
, ifp
->name
);
1903 if (PIM_DEBUG_IGMP_PACKETS
) {
1905 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1906 from_str
, ifp
->name
, igmp_msg_len
, checksum
,
1910 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1913 for (i
= 0; i
< num_groups
; ++i
) {
1914 struct in_addr rec_group
;
1919 int rec_num_sources
;
1924 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1927 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1928 from_str
, ifp
->name
);
1932 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1934 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1935 rec_num_sources
= ntohs(*(
1936 uint16_t *)(group_record
1937 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1940 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1941 sizeof(struct in_addr
));
1943 if (PIM_DEBUG_IGMP_PACKETS
) {
1945 "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1946 from_str
, ifp
->name
, i
, rec_type
,
1947 rec_auxdatalen
, rec_num_sources
,
1948 inet_ntoa(rec_group
));
1953 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1955 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1957 if ((src
+ 4) > report_pastend
) {
1959 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1960 from_str
, ifp
->name
);
1964 if (PIM_DEBUG_IGMP_PACKETS
) {
1967 if (!inet_ntop(AF_INET
, src
, src_str
,
1969 sprintf(src_str
, "<source?>");
1972 "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1973 from_str
, ifp
->name
, i
,
1974 inet_ntoa(rec_group
), src_str
);
1976 } /* for (sources) */
1979 lncb
.family
= AF_INET
;
1980 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1981 lncb
.prefixlen
= 24;
1984 g
.u
.prefix4
= rec_group
;
1987 * If we receive a igmp report with the group in 224.0.0.0/24
1988 * then we should ignore it
1990 if (prefix_match(&lncb
, &g
))
1995 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1996 igmpv3_report_isin(igmp
, from
, rec_group
,
1998 (struct in_addr
*)sources
);
2000 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
2002 igmp
, from
, rec_group
, rec_num_sources
,
2003 (struct in_addr
*)sources
, 0);
2005 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
2006 igmpv3_report_toin(igmp
, from
, rec_group
,
2008 (struct in_addr
*)sources
);
2010 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
2011 igmpv3_report_toex(igmp
, from
, rec_group
,
2013 (struct in_addr
*)sources
);
2015 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2016 igmpv3_report_allow(igmp
, from
, rec_group
,
2018 (struct in_addr
*)sources
);
2020 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2021 igmpv3_report_block(igmp
, from
, rec_group
,
2023 (struct in_addr
*)sources
);
2027 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2028 from_str
, ifp
->name
, rec_type
);
2032 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2035 } /* for (group records) */